Merge branch 'th/promisor-quiet-per-repo'

The "promisor.quiet" configuration variable was not used from
relevant submodules when commands like "grep --recurse-submodules"
triggered a lazy fetch, which has been corrected.

* th/promisor-quiet-per-repo:
  promisor-remote: fix promisor.quiet to use the correct repository
diff --git a/.github/workflows/check-style.yml b/.github/workflows/check-style.yml
index 19a145d..108a2de 100644
--- a/.github/workflows/check-style.yml
+++ b/.github/workflows/check-style.yml
@@ -20,7 +20,7 @@
       jobname: ClangFormat
     runs-on: ubuntu-latest
     steps:
-    - uses: actions/checkout@v5
+    - uses: actions/checkout@v6
       with:
         fetch-depth: 0
 
diff --git a/.github/workflows/check-whitespace.yml b/.github/workflows/check-whitespace.yml
index 928fd4c..ea6f49f 100644
--- a/.github/workflows/check-whitespace.yml
+++ b/.github/workflows/check-whitespace.yml
@@ -19,7 +19,7 @@
   check-whitespace:
     runs-on: ubuntu-latest
     steps:
-    - uses: actions/checkout@v5
+    - uses: actions/checkout@v6
       with:
         fetch-depth: 0
 
diff --git a/.github/workflows/coverity.yml b/.github/workflows/coverity.yml
index cfa17d3..58a78f1 100644
--- a/.github/workflows/coverity.yml
+++ b/.github/workflows/coverity.yml
@@ -38,10 +38,10 @@
       COVERITY_LANGUAGE: cxx
       COVERITY_PLATFORM: overridden-below
     steps:
-      - uses: actions/checkout@v5
+      - uses: actions/checkout@v6
       - name: install minimal Git for Windows SDK
         if: contains(matrix.os, 'windows')
-        uses: git-for-windows/setup-git-for-windows-sdk@v1
+        uses: git-for-windows/setup-git-for-windows-sdk@v2
       - run: ci/install-dependencies.sh
         if: contains(matrix.os, 'ubuntu') || contains(matrix.os, 'macos')
         env:
@@ -98,7 +98,7 @@
       # A cache miss will add ~30s to create, but a cache hit will save minutes.
       - name: restore the Coverity Build Tool
         id: cache
-        uses: actions/cache/restore@v4
+        uses: actions/cache/restore@v5
         with:
           path: ${{ runner.temp }}/cov-analysis
           key: cov-build-${{ env.COVERITY_LANGUAGE }}-${{ env.COVERITY_PLATFORM }}-${{ steps.lookup.outputs.hash }}
@@ -141,7 +141,7 @@
           esac
       - name: cache the Coverity Build Tool
         if: steps.cache.outputs.cache-hit != 'true'
-        uses: actions/cache/save@v4
+        uses: actions/cache/save@v5
         with:
           path: ${{ runner.temp }}/cov-analysis
           key: cov-build-${{ env.COVERITY_LANGUAGE }}-${{ env.COVERITY_PLATFORM }}-${{ steps.lookup.outputs.hash }}
diff --git a/.github/workflows/l10n.yml b/.github/workflows/l10n.yml
index 95e5513..114a12a 100644
--- a/.github/workflows/l10n.yml
+++ b/.github/workflows/l10n.yml
@@ -92,7 +92,7 @@
           cat git-po-helper.out
           exit $exit_code
       - name: Create comment in pull request for report
-        uses: mshick/add-pr-comment@v2
+        uses: mshick/add-pr-comment@v3
         if: >-
           always() &&
           github.event_name == 'pull_request_target' &&
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index 826f2f5..3da5326 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -63,7 +63,7 @@
           echo "skip_concurrent=$skip_concurrent" >>$GITHUB_OUTPUT
       - name: skip if the commit or tree was already tested
         id: skip-if-redundant
-        uses: actions/github-script@v8
+        uses: actions/github-script@v9
         if: steps.check-ref.outputs.enabled == 'yes'
         with:
           github-token: ${{secrets.GITHUB_TOKEN}}
@@ -112,8 +112,8 @@
       group: windows-build-${{ github.ref }}
       cancel-in-progress: ${{ needs.ci-config.outputs.skip_concurrent == 'yes' }}
     steps:
-    - uses: actions/checkout@v5
-    - uses: git-for-windows/setup-git-for-windows-sdk@v1
+    - uses: actions/checkout@v6
+    - uses: git-for-windows/setup-git-for-windows-sdk@v2
     - name: build
       shell: bash
       env:
@@ -123,7 +123,7 @@
     - name: zip up tracked files
       run: git archive -o artifacts/tracked.tar.gz HEAD
     - name: upload tracked files and build artifacts
-      uses: actions/upload-artifact@v5
+      uses: actions/upload-artifact@v7
       with:
         name: windows-artifacts
         path: artifacts
@@ -140,14 +140,14 @@
       cancel-in-progress: ${{ needs.ci-config.outputs.skip_concurrent == 'yes' }}
     steps:
     - name: download tracked files and build artifacts
-      uses: actions/download-artifact@v6
+      uses: actions/download-artifact@v8
       with:
         name: windows-artifacts
         path: ${{github.workspace}}
     - name: extract tracked files and build artifacts
       shell: bash
       run: tar xf artifacts.tar.gz && tar xf tracked.tar.gz
-    - uses: git-for-windows/setup-git-for-windows-sdk@v1
+    - uses: git-for-windows/setup-git-for-windows-sdk@v2
     - name: test
       shell: bash
       run: . /etc/profile && ci/run-test-slice.sh $((${{matrix.nr}} + 1)) 10
@@ -157,7 +157,7 @@
       run: ci/print-test-failures.sh
     - name: Upload failed tests' directories
       if: failure() && env.FAILED_TEST_ARTIFACTS != ''
-      uses: actions/upload-artifact@v5
+      uses: actions/upload-artifact@v7
       with:
         name: failed-tests-windows-${{ matrix.nr }}
         path: ${{env.FAILED_TEST_ARTIFACTS}}
@@ -173,10 +173,10 @@
       group: vs-build-${{ github.ref }}
       cancel-in-progress: ${{ needs.ci-config.outputs.skip_concurrent == 'yes' }}
     steps:
-    - uses: actions/checkout@v5
-    - uses: git-for-windows/setup-git-for-windows-sdk@v1
+    - uses: actions/checkout@v6
+    - uses: git-for-windows/setup-git-for-windows-sdk@v2
     - name: initialize vcpkg
-      uses: actions/checkout@v5
+      uses: actions/checkout@v6
       with:
         repository: 'microsoft/vcpkg'
         path: 'compat/vcbuild/vcpkg'
@@ -186,7 +186,7 @@
         repository: git/git
         definitionId: 9
     - name: add msbuild to PATH
-      uses: microsoft/setup-msbuild@v2
+      uses: microsoft/setup-msbuild@v3
     - name: copy dlls to root
       shell: cmd
       run: compat\vcbuild\vcpkg_copy_dlls.bat release
@@ -208,7 +208,7 @@
     - name: zip up tracked files
       run: git archive -o artifacts/tracked.tar.gz HEAD
     - name: upload tracked files and build artifacts
-      uses: actions/upload-artifact@v5
+      uses: actions/upload-artifact@v7
       with:
         name: vs-artifacts
         path: artifacts
@@ -224,9 +224,9 @@
       group: vs-test-${{ matrix.nr }}-${{ github.ref }}
       cancel-in-progress: ${{ needs.ci-config.outputs.skip_concurrent == 'yes' }}
     steps:
-    - uses: git-for-windows/setup-git-for-windows-sdk@v1
+    - uses: git-for-windows/setup-git-for-windows-sdk@v2
     - name: download tracked files and build artifacts
-      uses: actions/download-artifact@v6
+      uses: actions/download-artifact@v8
       with:
         name: vs-artifacts
         path: ${{github.workspace}}
@@ -244,7 +244,7 @@
       run: ci/print-test-failures.sh
     - name: Upload failed tests' directories
       if: failure() && env.FAILED_TEST_ARTIFACTS != ''
-      uses: actions/upload-artifact@v5
+      uses: actions/upload-artifact@v7
       with:
         name: failed-tests-windows-vs-${{ matrix.nr }}
         path: ${{env.FAILED_TEST_ARTIFACTS}}
@@ -258,7 +258,7 @@
       group: windows-meson-build-${{ github.ref }}
       cancel-in-progress: ${{ needs.ci-config.outputs.skip_concurrent == 'yes' }}
     steps:
-    - uses: actions/checkout@v5
+    - uses: actions/checkout@v6
     - uses: actions/setup-python@v6
     - name: Set up dependencies
       shell: pwsh
@@ -270,7 +270,7 @@
       shell: pwsh
       run: meson compile -C build
     - name: Upload build artifacts
-      uses: actions/upload-artifact@v5
+      uses: actions/upload-artifact@v7
       with:
         name: windows-meson-artifacts
         path: build
@@ -286,13 +286,13 @@
       group: windows-meson-test-${{ matrix.nr }}-${{ github.ref }}
       cancel-in-progress: ${{ needs.ci-config.outputs.skip_concurrent == 'yes' }}
     steps:
-    - uses: actions/checkout@v5
+    - uses: actions/checkout@v6
     - uses: actions/setup-python@v6
     - name: Set up dependencies
       shell: pwsh
       run: pip install meson ninja
     - name: Download build artifacts
-      uses: actions/download-artifact@v6
+      uses: actions/download-artifact@v8
       with:
         name: windows-meson-artifacts
         path: build
@@ -305,7 +305,7 @@
       run: ci/print-test-failures.sh
     - name: Upload failed tests' directories
       if: failure() && env.FAILED_TEST_ARTIFACTS != ''
-      uses: actions/upload-artifact@v4
+      uses: actions/upload-artifact@v7
       with:
         name: failed-tests-windows-meson-${{ matrix.nr }}
         path: ${{env.FAILED_TEST_ARTIFACTS}}
@@ -341,7 +341,7 @@
       TEST_OUTPUT_DIRECTORY: ${{github.workspace}}/t
     runs-on: ${{matrix.vector.pool}}
     steps:
-    - uses: actions/checkout@v5
+    - uses: actions/checkout@v6
     - run: ci/install-dependencies.sh
     - run: ci/run-build-and-tests.sh
     - name: print test failures
@@ -349,7 +349,7 @@
       run: ci/print-test-failures.sh
     - name: Upload failed tests' directories
       if: failure() && env.FAILED_TEST_ARTIFACTS != ''
-      uses: actions/upload-artifact@v5
+      uses: actions/upload-artifact@v7
       with:
         name: failed-tests-${{matrix.vector.jobname}}
         path: ${{env.FAILED_TEST_ARTIFACTS}}
@@ -362,7 +362,7 @@
       CI_JOB_IMAGE: ubuntu-latest
     runs-on: ubuntu-latest
     steps:
-    - uses: actions/checkout@v5
+    - uses: actions/checkout@v6
     - run: ci/install-dependencies.sh
     - run: ci/run-build-and-minimal-fuzzers.sh
   dockerized:
@@ -439,7 +439,7 @@
         else
           apt-get -q update && apt-get -q -y install git
         fi
-    - uses: actions/checkout@v5
+    - uses: actions/checkout@v6
     - run: ci/install-dependencies.sh
     - run: useradd builder --create-home
     - run: chown -R builder .
@@ -449,7 +449,7 @@
       run: sudo --preserve-env --set-home --user=builder ci/print-test-failures.sh
     - name: Upload failed tests' directories
       if: failure() && env.FAILED_TEST_ARTIFACTS != ''
-      uses: actions/upload-artifact@v5
+      uses: actions/upload-artifact@v7
       with:
         name: failed-tests-${{matrix.vector.jobname}}
         path: ${{env.FAILED_TEST_ARTIFACTS}}
@@ -464,7 +464,7 @@
       group: static-analysis-${{ github.ref }}
       cancel-in-progress: ${{ needs.ci-config.outputs.skip_concurrent == 'yes' }}
     steps:
-    - uses: actions/checkout@v5
+    - uses: actions/checkout@v6
     - run: ci/install-dependencies.sh
     - run: ci/run-static-analysis.sh
     - run: ci/check-directional-formatting.bash
@@ -480,7 +480,7 @@
       group: rust-analysis-${{ github.ref }}
       cancel-in-progress: ${{ needs.ci-config.outputs.skip_concurrent == 'yes' }}
     steps:
-    - uses: actions/checkout@v4
+    - uses: actions/checkout@v6
     - run: ci/install-dependencies.sh
     - run: ci/run-rust-checks.sh
   sparse:
@@ -494,7 +494,7 @@
       group: sparse-${{ github.ref }}
       cancel-in-progress: ${{ needs.ci-config.outputs.skip_concurrent == 'yes' }}
     steps:
-    - uses: actions/checkout@v5
+    - uses: actions/checkout@v6
     - name: Install other dependencies
       run: ci/install-dependencies.sh
     - run: make sparse
@@ -510,6 +510,6 @@
       CI_JOB_IMAGE: ubuntu-latest
     runs-on: ubuntu-latest
     steps:
-    - uses: actions/checkout@v5
+    - uses: actions/checkout@v6
     - run: ci/install-dependencies.sh
     - run: ci/test-documentation.sh
diff --git a/.gitignore b/.gitignore
index 24635cf..4da58c6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -71,6 +71,7 @@
 /git-for-each-ref
 /git-for-each-repo
 /git-format-patch
+/git-format-rev
 /git-fsck
 /git-fsck-objects
 /git-fsmonitor--daemon
@@ -182,6 +183,7 @@
 /git-update-server-info
 /git-upload-archive
 /git-upload-pack
+/git-url-parse
 /git-var
 /git-verify-commit
 /git-verify-pack
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 83ec786..e0b9a0d 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -79,7 +79,7 @@
   stage: test
   needs: [ ]
   tags:
-    - saas-macos-medium-m1
+    - saas-macos-large-m2pro
   variables:
     TEST_OUTPUT_DIRECTORY: "/Volumes/RAMDisk"
   before_script:
@@ -104,10 +104,10 @@
         image: macos-15-xcode-16
         CC: clang
       - jobname: osx-reftable
-        image: macos-15-xcode-16
+        image: macos-26-xcode-26
         CC: clang
       - jobname: osx-meson
-        image: macos-15-xcode-16
+        image: macos-26-xcode-26
         CC: clang
   artifacts:
     paths:
diff --git a/Documentation/BreakingChanges.adoc b/Documentation/BreakingChanges.adoc
index f814450..73bb939 100644
--- a/Documentation/BreakingChanges.adoc
+++ b/Documentation/BreakingChanges.adoc
@@ -190,7 +190,7 @@
 1. Initially, with Git 2.52, support for Rust will be auto-detected by Meson and
    disabled in our Makefile so that the project can sort out the initial
    infrastructure.
-2. In Git 2.53, both build systems will default-enable support for Rust.
+2. In Git 2.55, both build systems will default-enable support for Rust.
    Consequently, builds will break by default if Rust is not available on the
    build host. The use of Rust can still be explicitly disabled via build
    flags.
@@ -216,6 +216,30 @@
 release. This evaluation will also take into account our own experience with
 how painful it is to keep Rust an optional component.
 
+* The default value of `safe.bareRepository` will change from `all` to
+  `explicit`. It is all too easy for an attacker to trick a user into cloning a
+  repository that contains an embedded bare repository with malicious hooks
+  configured. If the user enters that subdirectory and runs any Git command, Git
+  discovers the bare repository and the hooks fire. The user does not even need
+  to run a Git command explicitly: many shell prompts run `git status` in the
+  background to display branch and dirty state information, and `git status` in
+  turn may invoke the fsmonitor hook if so configured, making the user
+  vulnerable the moment they `cd` into the directory. The `safe.bareRepository`
+  configuration variable was introduced in 8959555cee (setup_git_directory():
+  add an owner check for the top-level directory, 2022-03-02) with a default of
+  `all` to preserve backwards compatibility.
++
+Changing the default to `explicit` means that Git will refuse to work with bare
+repositories that are discovered implicitly by walking up the directory tree.
+Bare repositories specified explicitly via the `--git-dir` command-line option
+or the `GIT_DIR` environment variable continue to work regardless of this
+setting. Repositories that look like a `.git` directory, a worktree, or a
+submodule directory are also unaffected.
++
+Users who rely on implicit discovery of bare repositories can restore the
+previous behavior by setting `safe.bareRepository=all` in their global or
+system configuration.
+
 === Removals
 
 * Support for grafting commits has long been superseded by git-replace(1).
diff --git a/Documentation/CodingGuidelines b/Documentation/CodingGuidelines
index b867075..c06f5d3 100644
--- a/Documentation/CodingGuidelines
+++ b/Documentation/CodingGuidelines
@@ -668,6 +668,18 @@
    unsigned other_field:1;
    unsigned field_with_longer_name:1;
 
+ - When a function `F` accepts flags, those flags should be defined as `enum
+   F_flags`. Individual flag definitions should start with `F` and be in
+   all-uppercase letters. Flag values should be represented via bit shifts.
+   E.g.
+
+        enum frobnicate_flags {
+                FROBNICATE_FOO = (1 << 0),
+                FROBNICATE_BAR = (1 << 1),
+        };
+
+        int frobnicate(enum frobnicate_flags flags);
+
  - Array names should be named in the singular form if the individual items are
    subject of use. E.g.:
 
@@ -676,11 +688,17 @@
          walk_dog(dog[1]);
 
    Cases where the array is employed as a whole rather than as its unit parts,
-   the plural forms is preferable. E.g:
+   the plural form is preferable. E.g:
 
          char *dogs[] = ...;
          walk_all_dogs(dogs);
 
+ - For file timestamps, do not use "st_mtim" (and other timestamp
+   members in "struct stat") unconditionally; not everybody is POSIX
+   (grep for USE_ST_TIMESPEC).  If you only need a timestamp in whole
+   second resolution, "st_mtime" should work fine everywhere.
+
+
 For Perl programs:
 
  - Most of the C guidelines above apply.
diff --git a/Documentation/RelNotes/2.54.0.adoc b/Documentation/RelNotes/2.54.0.adoc
index 04c038f..7bfc351 100644
--- a/Documentation/RelNotes/2.54.0.adoc
+++ b/Documentation/RelNotes/2.54.0.adoc
@@ -36,7 +36,7 @@
  * Extend the alias configuration syntax to allow aliases using
    characters outside ASCII alphanumeric (plus '-').
 
- * A signature on a commit that was GPG signed long time ago ought to
+ * A signature on a commit that was GPG signed a long time ago ought to
    be still valid after the key that was used to sign it has expired,
    but we showed them in alarming red.
 
@@ -88,7 +88,7 @@
  * "git repo structure" command learns to report maximum values on
    various aspects of objects it inspects.
 
- * "git rebase" learns "--trailer" command to drive the
+ * "git rebase" learns "--trailer" option to drive the
    interpret-trailers machinery.
 
  * "git fast-import" learned to optionally replace signature on
@@ -119,6 +119,14 @@
  * "git replay" (experimental) learns, in addition to "pick" and
    "replay", a new operating mode "revert".
 
+ * "git replay" now supports replaying down to the root commit.
+
+ * Handling of signed commits and tags in fast-import has been made more
+   configurable.
+
+ * "git config list" is the official way to spell "git config -l" and
+   "git config --list".  Use it to update the documentation.
+
 
 Performance, Internal Implementation, Development Support etc.
 --------------------------------------------------------------
@@ -199,7 +207,7 @@
    from getting added.
 
  * The core.attributesfile is intended to be set per repository, but
-   were kept track of by a single global variable in-core, which has
+   was kept track of by a single global variable in-core, which has
    been corrected by moving it to per-repository data structure.
 
  * Use the hook API to replace ad-hoc invocation of hook scripts via
@@ -224,8 +232,8 @@
  * Reduce dependence on the global the_hash_algo and the_repository
    variables of wt-status code path.
 
-  * The way combined list-object filter options are parsed has been
-    revamped.
+ * The way combined list-object filter options are parsed has been
+   revamped.
 
  * Editorconfig filename patterns were specified incorrectly, making
    many source files inside subdirectories unaffected, which has been
@@ -264,7 +272,7 @@
  * split-index.c has been updated to not use the global the_repository
    and the_hash_algo variables.
 
- * The unsigned integer that is used as an bitset to specify the kind
+ * The unsigned integer that is used as a bitset to specify the kind
    of branches interpret_branch_name() function has been changed to
    use a dedicated enum type.
 
@@ -274,7 +282,7 @@
  * Code paths that loop over another array to push each element into a
    strvec have been rewritten to use strvec_pushv() instead.
 
- * In case homebrew breaks REG_ENHANCED again, leave a in-code comment
+ * In case homebrew breaks REG_ENHANCED again, leave an in-code comment
    to suggest use of our replacement regex as a workaround.
 
  * MinGW build updates.
@@ -283,6 +291,42 @@
    is buggy and breaks our existing tests, which unfortunately
    have been rewritten to avoid triggering the bug.
 
+ * Object name handling (disambiguation and abbreviation) has been
+   refactored to be backend-generic, moving logic into the respective
+   object database backends.
+
+ * pack-objects's --stdin-packs=follow mode learns to handle
+   excluded-but-open packs.
+
+ * A few code paths that spawned child processes for network
+   connection weren't wait(2)ing for their children and letting "init"
+   reap them instead; they have been tightened.
+
+ * Adjust the codebase for C23 that changes functions like strchr()
+   that discarded constness when they return a pointer into a const
+   string to preserve constness.
+
+ * A handful of inappropriate uses of the_repository have been
+   rewritten to use the right repository structure instance in the
+   read-cache.c codepath.
+
+ * Internals of "git fsck" have been refactored to not depend on the
+   global `the_repository` variable.
+
+ * Reduce dependency on `the_repository` in add-patch.c file.
+
+ * The way the "git log -L<range>:<file>" feature is bolted onto the
+   log/diff machinery is being reworked a bit to make the feature
+   compatible with more diff options, like -S/G.
+
+ * Further work to adjust the codebase for C23 that changes functions
+   like strchr() that discarded constness when they return a pointer into
+   a const string to preserve constness.
+
+ * "git rev-list --maximal-only" has been optimized by borrowing the
+   logic used by "git show-branch --independent", which computes the
+   same kind of information much more efficiently.
+
 
 Fixes since v2.53
 -----------------
@@ -338,7 +382,7 @@
    (merge f4eff7116d ps/pack-concat-wo-backfill later to maint).
 
  * "git switch <name>", in an attempt to create a local branch <name>
-   after a remote tracking branch of the same name gave an advise
+   after a remote tracking branch of the same name gave an advice
    message to disambiguate using "git checkout", which has been
    updated to use "git switch".
    (merge 12fee11f21 jc/checkout-switch-restore later to maint).
@@ -352,7 +396,7 @@
  * "git format-patch --from=<me>" did not honor the command line
    option when writing out the cover letter, which has been corrected.
 
- * Update build precedure for mergetool documentation in meson-based builds.
+ * Update build procedure for mergetool documentation in meson-based builds.
    (merge 58e4eeeeb5 pw/meson-doc-mergetool later to maint).
 
  * An earlier attempt to optimize "git subtree" discarded too much
@@ -461,6 +505,33 @@
    refspec is a single-object refspec, which has been corrected.
    (merge 4e5dc601dd kj/refspec-parsing-outside-repository later to maint).
 
+ * Fix a regression in writing the commit-graph where commits with dates
+   exceeding 34 bits (beyond year 2514) could cause an underflow and
+   crash Git during the generation data overflow chunk writing.
+
+ * The value of a wrong pointer variable was referenced in an error
+   message that reported that it shouldn't be NULL.
+   (merge 753ecf4205 yc/path-walk-fix-error-reporting later to maint).
+
+ * The check in "receive-pack" to prevent a checked out branch from
+   getting updated via updateInstead mechanism has been corrected.
+
+ * "git backfill" is capable of auto-detecting a sparsely checked out
+   working tree, which was broken.
+   (merge 339eba65a7 th/backfill-auto-detect-sparseness-fix later to maint).
+
+ * add_files_to_cache() used diff_files() to detect only the paths that
+   are different between the index and the working tree and add them,
+   which does not need rename detection, which interfered with unnecessary
+   conflicts.
+   (merge c0ce43376b ng/add-files-to-cache-wo-rename later to maint).
+
+ * Doc mark-up update for entries in the glossary with bulleted lists.
+   (merge a65cbd87ea jk/doc-markup-sub-list-indentation later to maint).
+
+ * CI dependency updates.
+   (merge 4bdb17e3a8 jc/ci-github-actions-use-checkout-v5 later to maint).
+
  * Other code cleanup, docfix, build fix, etc.
    (merge d79fff4a11 jk/remote-tracking-ref-leakfix later to maint).
    (merge 7a747f972d dd/t5403-modernise later to maint).
@@ -512,3 +583,6 @@
    (merge 37182267a0 kh/doc-interpret-trailers-1 later to maint).
    (merge f64c50e768 jc/rerere-modern-strbuf-handling later to maint).
    (merge 699248d89e th/t8003-unhide-git-failures later to maint).
+   (merge d8e34f971b za/t2000-modernise later to maint).
+   (merge 849988bc74 th/t6101-unhide-git-failures later to maint).
+   (merge 0f0ce07625 sp/doc-gitignore-oowt later to maint).
diff --git a/Documentation/RelNotes/2.54.1.adoc b/Documentation/RelNotes/2.54.1.adoc
new file mode 100644
index 0000000..f561dbb
--- /dev/null
+++ b/Documentation/RelNotes/2.54.1.adoc
@@ -0,0 +1,29 @@
+Git v2.54.1 Release Notes
+=========================
+
+This release is primarily to merge fixes accumulated on the 'master'
+front to prepare for 2.55 release that are still relevant to 2.54.x
+maintenance track.
+
+Fixes since v2.54
+-----------------
+
+ * Headers from glibc 2.43 when used with clang does not allow
+   disabling C11 language features, causing build failures..
+
+ * Revert a recent change that introduced a regression to help mksh users.
+
+ * Update various GitHub Actions versions.
+
+ * Avoid hitting the pathname limit for socks proxy socket during the
+   test.
+
+ * To help Windows 10 installations, avoid removing files whose
+   contents are still mmap()'ed.
+
+ * Stop using unmaintained custom allocator in Windows build which was
+   the last user of the code.
+
+ * Further update to the i18n alias support to avoid regressions.
+
+Also contains minor documentation updates and code clean-ups.
diff --git a/Documentation/RelNotes/2.55.0.adoc b/Documentation/RelNotes/2.55.0.adoc
new file mode 100644
index 0000000..937ac48
--- /dev/null
+++ b/Documentation/RelNotes/2.55.0.adoc
@@ -0,0 +1,288 @@
+Git v2.55 Release Notes
+=======================
+
+UI, Workflows & Features
+------------------------
+
+ * Hook scripts defined via the configuration system can now be
+   configured to run in parallel.
+
+ * The userdiff driver for the Scheme language has been extended to
+   cover other Lisp dialects.
+
+ * Terminal control sequences coming over the sideband while talking
+   to a remote repository are mostly disabled by default, except for
+   ANSI color escape sequences.
+
+ * "ort" merge backend improvements.
+
+ * "git checkout -m another-branch" was invented to deal with local
+   changes to paths that are different between the current and the new
+   branch, but it gave only one chance to resolve conflicts.  The command
+   was taught to create a stash to save the local changes.
+
+ * A new builtin "git format-rev" is introduced for pretty formatting
+   one revision expression per line or commit object names found in
+   running text.
+
+ * "git history" learned "fixup" command.
+
+ * The internal URL parsing logic has been made accessible via a new
+   subcommand "git url-parse".
+
+ * Misspelt proxy URL (e.g., httt://...) did not trigger any warning
+   or failure, which has been corrected.
+
+ * Document the fact that .git/info/exclude is shared across worktrees
+   linked to the same repository.
+
+ * The command line parser for "git diff" learned a few options take
+   only non-negative integers.
+
+ * The graph output from commands like "git log --graph" can now be
+   limited to a specified number of lanes, preventing overly wide output
+   in repositories with many branches.
+
+ * The fsmonitor daemon has been implemented for Linux.
+
+ * "git cat-file --batch" learns an in-line command "mailmap"
+   that lets the user toggle use of mailmap.
+
+ * The "git pack-objects --path-walk" traversal has been integrated
+   with several object filters, including blobless and sparse filters.
+
+
+Performance, Internal Implementation, Development Support etc.
+--------------------------------------------------------------
+
+ * Promisor remote handling has been refactored and fixed in
+   preparation for auto-configuration of advertised remotes.
+
+ * Rust support is enabled by default (but still allows opting out) in
+   some future version of Git.
+
+ * Preparation of the xdiff/ codebase to work with Rust.
+
+ * Use a larger buffer size in the code paths to ingest pack stream.
+
+ * Refactor service routines in the ref subsystem backends.
+
+ * Shrink wasted memory in Myers diff that does not account for common
+   prefix and suffix removal.
+
+ * Enable expensive tests to catch topics that may cause breakages on
+   integration branches closer to their origin in the contributor PR
+   builds.
+
+ * "git merge-base" optimization.
+
+ * The limit_list() function that is one of the core part of the
+   revision traversal infrastructure has been optimized by replacing
+   its use of linear list with priority queue.
+
+ * In a lazy clone, "git cherry" and "git grep" often fetch necessary
+   blob objects one by one from promisor remotes.  It has been corrected
+   to collect necessary object names and fetch them in bulk to gain
+   reasonable performance.
+
+ * The logic to determine that branches in an octopus merge are
+   independent has been optimized.
+
+ * The consistency checks for the files reference backend have been updated
+   to skip lock files earlier, avoiding unnecessary parsing of
+   intermediate files.
+
+ * The negotiation tip options in "git fetch" have been reworked to
+   allow requiring certain refs to be sent as "have" lines, and to
+   restrict negotiation to a specific set of refs.
+
+ * The repacking code has been refactored and compaction of MIDX layers
+   have been implemented, and incremental strategy that does not require
+   all-into-one repacking has been introduced.
+
+ * ODB transaction interface is being reworked to explicitly handle
+   object writes.
+
+ * Add a new odb "in-memory" source that is meant to only hold
+   tentative objects (like the virtual blob object that represents the
+   working tree file used by "git blame").
+
+ * Many uses of the_repository has been updated to use a more
+   appropriate struct repository instance in setup.c codepath.
+
+ * Revision traversal optimization.
+
+ * Build update.
+
+ * The logic to lazy-load trees from the commit-graph has been made
+   more robust by falling back to reading the commit object when
+   the commit-graph is no longer available.
+
+ * The "name" argument in git_connect() and related functions has been
+   converted to a "service" enum to improve type safety and clarify its
+   purpose.
+
+ * 'git restore --staged' has been optimized to avoid unnecessarily expanding
+   the sparse index when operating on paths within the sparse checkout
+   definition, by handling sparse directory entries at the tree level.
+
+ * "git stash -p" has been optimized by reusing cached index
+   entries in its temporary index, avoiding unnecessary lstat()
+   calls on unchanged files.
+
+ * The check for non-stale commits in the priority queue used by
+   `paint_down_to_common` and `ahead_behind` has been optimized by
+   replacing an O(N) scan with an O(1) counter, yielding performance
+   improvements in repositories with wide histories.
+
+
+Fixes since v2.54
+-----------------
+
+ * Code clean-up to use the right instance of a repository instance in
+   calls inside refs subsystem.
+   (merge 57c590feb9 sp/refs-reduce-the-repository later to maint).
+
+ * The check that implements the logic to see if an in-core cache-tree
+   is fully ready to write out a tree object was broken, which has
+   been corrected.
+   (merge 521731213c dl/cache-tree-fully-valid-fix later to maint).
+
+ * The test suite harness and many individual test scripts have been
+   updated to work correctly when 'set -e' is in effect, which helps
+   detect misspelled test commands.
+   (merge ffe8005b9d ps/test-set-e-clean later to maint).
+
+ * Revert a recent change that introduced a regression to help mksh users.
+
+ * Update various GitHub Actions versions.
+
+ * Avoid hitting the pathname limit for socks proxy socket during the
+   test..
+
+ * To help Windows 10 installations, avoid removing files whose
+   contents are still mmap()'ed.
+
+ * The 'git backfill' command now rejects revision-limiting options that
+   are incompatible with its operation, uses standard documentation for
+   revision ranges, and includes blobs from boundary commits by default
+   to improve performance of subsequent operations.
+   (merge a1ad4a0fca en/backfill-fixes-and-edges later to maint).
+
+ * "git grep" update.
+   (merge 9ff4b5ab1b rs/grep-column-only-match-fix later to maint).
+
+ * Headers from glibc 2.43 when used with clang does not allow
+   disabling C11 language features, causing build failures..
+
+ * The 'http.emptyAuth=auto' configuration now correctly attempts
+   Negotiate authentication before falling back to manual credentials.
+   This allows seamless Kerberos ticket-based authentication without
+   requiring users to explicitly set 'http.emptyAuth=true'.
+   (merge 4919938d28 mc/http-emptyauth-negotiate-fix later to maint).
+
+ * Ramifications of turning off commit-graph has been documented a bit
+   more clearly.
+   (merge 48c855bb8f kh/doc-commit-graph later to maint).
+
+ * "git rebase --update-refs", when used with an rebase.instructionFormat
+   with "%d" (describe) in it, tried to update local branch HEAD by
+   mistake, which has been corrected.
+   (merge 106b6885c7 ag/rebase-update-refs-limit-to-branches later to maint).
+
+ * Tweak the way how sideband messages from remote are printed while
+   we talk with a remote repository to avoid tickling terminal
+   emulator glitches.
+   (merge 31e8fcabd8 rs/sideband-clear-line-before-print later to maint).
+
+ * The configuration variable submodule.fetchJobs was not read correctly,
+   which has been corrected.
+   (merge aa45a5902f sj/submodule-update-clone-config-fix later to maint).
+
+ * Update code paths that assumed "unsigned long" was long enough for
+   "size_t".
+   (merge 7a094d68a2 js/objects-larger-than-4gb-on-windows later to maint).
+
+ * Stop using unmaintained custom allocator in Windows build which was
+   the last user of the code.
+
+ * The computation to shorten the filenames shown in diffstat measured
+   width of individual UTF-8 characters to add up, but forgot to take
+   into account error cases (e.g., an invalid UTF-8 sequence, or a
+   control character).
+   (merge 09d86a3b98 en/diffstat-utf8-truncation-fix later to maint).
+
+ * Some tests assume that bare repository accesses are by default
+   allowed; rewrite some of them to avoid the assumption, rewrite
+   others to explicitly set safe.bareRepository to allow them.
+   (merge 985b38ca6c js/adjust-tests-to-explicitly-access-bare-repo later to maint).
+
+ * Signing commit with custom encoding was passing the data to be
+   signed at a wrong stage in the pipeline, which has been corrected.
+   (merge 7735d7eee3 bc/sign-commit-with-custom-encoding later to maint).
+
+ * Further update to the i18n alias support to avoid regressions.
+
+ * "git fetch --deepen=<n>" in a full clone truncated the history to <n>
+   commits deep, which has been corrected to be a no-op instead.
+   (merge 2431f5e0e5 sp/shallow-deepen-on-non-shallow-repo-fix later to maint).
+
+ * "git maintenance" that goes background did not use the lockfile to
+   prevent multiple maintenance processes from running at the same
+   time, which has been corrected.
+   (merge 29364f1624 ps/maintenance-daemonize-lockfix later to maint).
+
+ * Remove ineffective strbuf presizing that would have computed an
+   allocation that would not have fit in the available memory anyway,
+   or too small due to integer wraparound to cause immediate automatic
+   growing.
+   (merge a9ce8526dc jk/pretty-no-strbuf-presizing later to maint).
+
+ * The HTTP walker misinterpreted the alternates file that gives an
+   absolute path when the server URL does not have the final slash
+   (i.e., "https://example.com" not "https://example.com/").
+   (merge b92387cd55 jk/dumb-http-alternate-fix later to maint).
+
+ * "git bisect" now uses the selected terms (e.g., old/new) more
+   consistently in its output.
+   (merge cb55991825 jr/bisect-custom-terms-in-output later to maint).
+
+ * Update GitLab CI jobs that exercise macOS.
+   (merge 62319b49bb ps/gitlab-ci-macOS-improvements later to maint).
+
+ * "Friday noon" asked in the morning on Sunday was parsed to be one
+   day before the specified time, which has been corrected.
+   (merge b809304101 ta/approxidate-noon-fix later to maint).
+
+ * The GIT_WORK_TREE variable prepared to invoke the push-to-checkout
+   hook was leaking into the environment even when there was no hook
+   used and broke the default push-to-deploy (i.e., let "git checkout"
+   update the working tree only when the working tree is clean).
+   (merge 44d04e4426 ar/receive-pack-worktree-env later to maint).
+
+ * A batch of documentation pages has been updated to use the modern
+   synopsis style.
+   (merge 2ef248ae45 ja/doc-synopsis-style-again later to maint).
+
+ * Other code cleanup, docfix, build fix, etc.
+   (merge 80f4b802e9 ja/doc-difftool-synopsis-style later to maint).
+   (merge b96490241e jc/doc-timestamps-in-stat later to maint).
+   (merge ef85286e51 ss/t7004-unhide-git-failures later to maint).
+   (merge 7584d10bc2 mf/format-patch-cover-letter-format-docfix later to maint).
+   (merge 8547908eb3 pw/rename-to-get-current-worktree later to maint).
+   (merge 890229b3f3 sg/t6112-unwanted-tilde-expansion-fix later to maint).
+   (merge ab9753e7bc kh/doc-restore-double-underscores-fix later to maint).
+   (merge 4a9e097228 za/t2000-modernise-more later to maint).
+   (merge b635fd0725 kh/doc-log-decorate-list later to maint).
+   (merge 65ea197dca jk/commit-sign-overflow-fix later to maint).
+   (merge 3ccb16052a jk/apply-leakfix later to maint).
+   (merge 5e6e8dc786 tb/pseudo-merge-bugfixes later to maint).
+   (merge 6d09e798bc pb/doc-diff-format-updates later to maint).
+   (merge 34a891a2d3 rs/trailer-fold-optim later to maint).
+   (merge 499f9048e0 ps/t3903-cover-stash-include-untracked later to maint).
+   (merge b56ab270aa jk/sq-dequote-cleanup later to maint).
+   (merge 29d9fdcf10 rs/use-builtin-add-overflow-explicitly-on-clang later to maint).
+   (merge d9982e8290 ed/check-connected-close-err-fd-2.53 later to maint).
+   (merge 1740cc35d0 ed/check-connected-close-err-fd later to maint).
+   (merge f4d7eb3d1c sp/doc-range-diff-takes-notes later to maint).
+   (merge 83e7f3bd2b kh/free-commit-list later to maint).
diff --git a/Documentation/asciidoc.conf.in b/Documentation/asciidoc.conf.in
index 31b883a..93c63b2 100644
--- a/Documentation/asciidoc.conf.in
+++ b/Documentation/asciidoc.conf.in
@@ -84,6 +84,9 @@
 [blockdef-open]
 synopsis-style=template="verseparagraph",filter="sed 's!&#8230;\\(\\]\\|$\\)!<phrase>\\0</phrase>!g;s!\\([\\[ |()]\\|^\\|\\]\\|&gt;\\)\\([-=a-zA-Z0-9:+@,\\/_^\\$.\\\\\\*]\\+\\|&#8230;\\)!\\1<literal>\\2</literal>!g;s!&lt;[-a-zA-Z0-9.]\\+&gt;!<emphasis>\\0</emphasis>!g'"
 
+[blockdef-listing]
+synopsis-style=template="verseparagraph",filter="sed 's!&#8230;\\(\\]\\|$\\)!<phrase>\\0</phrase>!g;s!\\([\\[ |()]\\|^\\|\\]\\|&gt;\\)\\([-=a-zA-Z0-9:+@,\\/_^\\$.\\\\\\*]\\+\\|&#8230;\\)!\\1<literal>\\2</literal>!g;s!&lt;[-a-zA-Z0-9.]\\+&gt;!<emphasis>\\0</emphasis>!g'"
+
 [paradef-default]
 synopsis-style=template="verseparagraph",filter="sed 's!&#8230;\\(\\]\\|$\\)!<phrase>\\0</phrase>!g;s!\\([\\[ |()]\\|^\\|\\]\\|&gt;\\)\\([-=a-zA-Z0-9:+@,\\/_^\\$.\\\\\\*]\\+\\|&#8230;\\)!\\1<literal>\\2</literal>!g;s!&lt;[-a-zA-Z0-9.]\\+&gt;!<emphasis>\\0</emphasis>!g'"
 endif::doctype-manpage[]
@@ -93,6 +96,9 @@
 [blockdef-open]
 synopsis-style=template="verseparagraph",filter="sed 's!&#8230;\\(\\]\\|$\\)!<span>\\0</span>!g;s!\\([\\[ |()]\\|^\\|\\]\\|&gt;\\)\\([-=a-zA-Z0-9:+@,\\/_^\\$.\\\\\\*]\\+\\|&#8230;\\)!\\1<code>\\2</code>!g;s!&lt;[-a-zA-Z0-9.]\\+&gt;!<em>\\0</em>!g'"
 
+[blockdef-listing]
+synopsis-style=template="verseparagraph",filter="sed 's!&#8230;\\(\\]\\|$\\)!<span>\\0</span>!g;s!\\([\\[ |()]\\|^\\|\\]\\|&gt;\\)\\([-=a-zA-Z0-9:+@,\\/_^\\$.\\\\\\*]\\+\\|&#8230;\\)!\\1<code>\\2</code>!g;s!&lt;[-a-zA-Z0-9.]\\+&gt;!<em>\\0</em>!g'"
+
 [paradef-default]
 synopsis-style=template="verseparagraph",filter="sed 's!&#8230;\\(\\]\\|$\\)!<span>\\0</span>!g;s!\\([\\[ |()]\\|^\\|\\]\\|&gt;\\)\\([-=a-zA-Z0-9:+@,\\/_^\\$.\\\\\\*]\\+\\|&#8230;\\)!\\1<code>\\2</code>!g;s!&lt;[-a-zA-Z0-9.]\\+&gt;!<em>\\0</em>!g'"
 endif::backend-xhtml11[]
diff --git a/Documentation/config.adoc b/Documentation/config.adoc
index 62eebe7..a80e7db 100644
--- a/Documentation/config.adoc
+++ b/Documentation/config.adoc
@@ -451,6 +451,8 @@
 
 include::config/help.adoc[]
 
+include::config/hook.adoc[]
+
 include::config/http.adoc[]
 
 include::config/i18n.adoc[]
@@ -523,6 +525,8 @@
 
 include::config/showbranch.adoc[]
 
+include::config/sideband.adoc[]
+
 include::config/sparse.adoc[]
 
 include::config/splitindex.adoc[]
diff --git a/Documentation/config/am.adoc b/Documentation/config/am.adoc
index e9561e1..250e6b5 100644
--- a/Documentation/config/am.adoc
+++ b/Documentation/config/am.adoc
@@ -1,11 +1,11 @@
-am.keepcr::
+`am.keepcr`::
 	If true, linkgit:git-am[1] will call linkgit:git-mailsplit[1]
 	for patches in mbox format with parameter `--keep-cr`. In this
 	case linkgit:git-mailsplit[1] will
 	not remove `\r` from lines ending with `\r\n`. Can be overridden
 	by giving `--no-keep-cr` from the command line.
 
-am.threeWay::
+`am.threeWay`::
 	By default, linkgit:git-am[1] will fail if the patch does not
 	apply cleanly. When set to true, this setting tells
 	linkgit:git-am[1] to fall back on 3-way merge if the patch
@@ -13,7 +13,7 @@
 	have those blobs available locally (equivalent to giving the
 	`--3way` option from the command line). Defaults to `false`.
 
-am.messageId::
+`am.messageId`::
 	Add a `Message-ID` trailer based on the email header to the
 	commit when using linkgit:git-am[1] (see
 	linkgit:git-interpret-trailers[1]). See also the `--message-id`
diff --git a/Documentation/config/apply.adoc b/Documentation/config/apply.adoc
index f9908e2..36fcea6 100644
--- a/Documentation/config/apply.adoc
+++ b/Documentation/config/apply.adoc
@@ -1,11 +1,16 @@
-apply.ignoreWhitespace::
-	When set to 'change', tells 'git apply' to ignore changes in
+`apply.ignoreWhitespace`::
+	When set to `change`, tells `git apply` to ignore changes in
 	whitespace, in the same way as the `--ignore-space-change`
 	option.
-	When set to one of: no, none, never, false, it tells 'git apply' to
+	When set to one of: `no`, `none`, `never`, `false`, it tells `git apply` to
 	respect all whitespace differences.
+ifndef::git-apply[]
 	See linkgit:git-apply[1].
+endif::git-apply[]
 
-apply.whitespace::
-	Tells 'git apply' how to handle whitespace, in the same way
-	as the `--whitespace` option. See linkgit:git-apply[1].
+`apply.whitespace`::
+	Tells `git apply` how to handle whitespace, in the same way
+	as the `--whitespace` option.
+ifndef::git-apply[]
+	See linkgit:git-apply[1].
+endif::git-apply[]
diff --git a/Documentation/config/bitmap-pseudo-merge.adoc b/Documentation/config/bitmap-pseudo-merge.adoc
index 1f264ec..6bf52c8 100644
--- a/Documentation/config/bitmap-pseudo-merge.adoc
+++ b/Documentation/config/bitmap-pseudo-merge.adoc
@@ -47,8 +47,8 @@
 bitmapPseudoMerge.<name>.sampleRate::
 	Determines the proportion of non-bitmapped commits (among
 	reference tips) which are selected for inclusion in an
-	unstable pseudo-merge bitmap. Must be between `0` and `1`
-	(inclusive). The default is `1`.
+	unstable pseudo-merge bitmap. Must be greater than `0` and
+	less than or equal to `1`. The default is `1`.
 
 bitmapPseudoMerge.<name>.threshold::
 	Determines the minimum age of non-bitmapped commits (among
diff --git a/Documentation/config/difftool.adoc b/Documentation/config/difftool.adoc
index 4f7d40c..1b8d483 100644
--- a/Documentation/config/difftool.adoc
+++ b/Documentation/config/difftool.adoc
@@ -1,43 +1,43 @@
-diff.tool::
+`diff.tool`::
 	Controls which diff tool is used by linkgit:git-difftool[1].
 	This variable overrides the value configured in `merge.tool`.
 	The list below shows the valid built-in values.
 	Any other value is treated as a custom diff tool and requires
-	that a corresponding difftool.<tool>.cmd variable is defined.
+	that a corresponding `difftool.<tool>.cmd` variable is defined.
 
-diff.guitool::
+`diff.guitool`::
 	Controls which diff tool is used by linkgit:git-difftool[1] when
-	the -g/--gui flag is specified. This variable overrides the value
+	the `-g`/`--gui` flag is specified. This variable overrides the value
 	configured in `merge.guitool`. The list below shows the valid
 	built-in values. Any other value is treated as a custom diff tool
-	and requires that a corresponding difftool.<guitool>.cmd variable
+	and requires that a corresponding `difftool.<guitool>.cmd` variable
 	is defined.
 
 include::{build_dir}/mergetools-diff.adoc[]
 
-difftool.<tool>.cmd::
+`difftool.<tool>.cmd`::
 	Specify the command to invoke the specified diff tool.
 	The specified command is evaluated in shell with the following
-	variables available:  'LOCAL' is set to the name of the temporary
-	file containing the contents of the diff pre-image and 'REMOTE'
+	variables available: `LOCAL` is set to the name of the temporary
+	file containing the contents of the diff pre-image and `REMOTE`
 	is set to the name of the temporary file containing the contents
 	of the diff post-image.
 +
 See the `--tool=<tool>` option in linkgit:git-difftool[1] for more details.
 
-difftool.<tool>.path::
+`difftool.<tool>.path`::
 	Override the path for the given tool.  This is useful in case
 	your tool is not in the PATH.
 
-difftool.trustExitCode::
+`difftool.trustExitCode`::
 	Exit difftool if the invoked diff tool returns a non-zero exit status.
 +
 See the `--trust-exit-code` option in linkgit:git-difftool[1] for more details.
 
-difftool.prompt::
+`difftool.prompt`::
 	Prompt before each invocation of the diff tool.
 
-difftool.guiDefault::
+`difftool.guiDefault`::
 	Set `true` to use the `diff.guitool` by default (equivalent to specifying
 	the `--gui` argument), or `auto` to select `diff.guitool` or `diff.tool`
 	depending on the presence of a `DISPLAY` environment variable value. The
diff --git a/Documentation/config/fetch.adoc b/Documentation/config/fetch.adoc
index cd40db0..04ac909 100644
--- a/Documentation/config/fetch.adoc
+++ b/Documentation/config/fetch.adoc
@@ -76,7 +76,7 @@
 	default is `skipping`.  Unknown values will cause `git fetch` to
 	error out.
 +
-See also the `--negotiate-only` and `--negotiation-tip` options to
+See also the `--negotiate-only` and `--negotiation-restrict` options to
 linkgit:git-fetch[1].
 
 `fetch.showForcedUpdates`::
diff --git a/Documentation/config/format.adoc b/Documentation/config/format.adoc
index dbd1862..95d1913 100644
--- a/Documentation/config/format.adoc
+++ b/Documentation/config/format.adoc
@@ -102,7 +102,7 @@
 	Default is false.
 
 format.commitListFormat::
-	When the `--cover-letter-format` option is not given, `format-patch`
+	When the `--commit-list-format` option is not given, `format-patch`
 	uses the value of this variable to decide how to format the entry of
 	each commit. Defaults to `shortlog`.
 
diff --git a/Documentation/config/fsmonitor--daemon.adoc b/Documentation/config/fsmonitor--daemon.adoc
index 671f9b9..6f8386e 100644
--- a/Documentation/config/fsmonitor--daemon.adoc
+++ b/Documentation/config/fsmonitor--daemon.adoc
@@ -4,8 +4,8 @@
     behavior.  Only respected when `core.fsmonitor` is set to `true`.
 
 fsmonitor.socketDir::
-    This Mac OS-specific option, if set, specifies the directory in
+    This Mac OS and Linux-specific option, if set, specifies the directory in
     which to create the Unix domain socket used for communication
     between the fsmonitor daemon and various Git commands. The directory must
-    reside on a native Mac OS filesystem.  Only respected when `core.fsmonitor`
+    reside on a native filesystem.  Only respected when `core.fsmonitor`
     is set to `true`.
diff --git a/Documentation/config/grep.adoc b/Documentation/config/grep.adoc
index 10041f2..83d4b76 100644
--- a/Documentation/config/grep.adoc
+++ b/Documentation/config/grep.adoc
@@ -1,28 +1,28 @@
-grep.lineNumber::
-	If set to true, enable `-n` option by default.
+`grep.lineNumber`::
+	If set to `true`, enable `-n` option by default.
 
-grep.column::
-	If set to true, enable the `--column` option by default.
+`grep.column`::
+	If set to `true`, enable the `--column` option by default.
 
-grep.patternType::
-	Set the default matching behavior. Using a value of 'basic', 'extended',
-	'fixed', or 'perl' will enable the `--basic-regexp`, `--extended-regexp`,
+`grep.patternType`::
+	Set the default matching behavior. Using a value of `basic`, `extended`,
+	`fixed`, or `perl` will enable the `--basic-regexp`, `--extended-regexp`,
 	`--fixed-strings`, or `--perl-regexp` option accordingly, while the
-	value 'default' will use the `grep.extendedRegexp` option to choose
-	between 'basic' and 'extended'.
+	value `default` will use the `grep.extendedRegexp` option to choose
+	between `basic` and `extended`.
 
-grep.extendedRegexp::
-	If set to true, enable `--extended-regexp` option by default. This
+`grep.extendedRegexp`::
+	If set to `true`, enable `--extended-regexp` option by default. This
 	option is ignored when the `grep.patternType` option is set to a value
-	other than 'default'.
+	other than `default`.
 
-grep.threads::
+`grep.threads`::
 	Number of grep worker threads to use. If unset (or set to 0), Git will
 	use as many threads as the number of logical cores available.
 
-grep.fullName::
-	If set to true, enable `--full-name` option by default.
+`grep.fullName`::
+	If set to `true`, enable `--full-name` option by default.
 
-grep.fallbackToNoIndex::
-	If set to true, fall back to `git grep --no-index` if `git grep`
-	is executed outside of a git repository.  Defaults to false.
+`grep.fallbackToNoIndex`::
+	If set to `true`, fall back to `git grep --no-index` if `git grep`
+	is executed outside of a git repository.  Defaults to `false`.
diff --git a/Documentation/config/hook.adoc b/Documentation/config/hook.adoc
index 9e78f26..083dc60 100644
--- a/Documentation/config/hook.adoc
+++ b/Documentation/config/hook.adoc
@@ -1,10 +1,17 @@
+ifdef::git-hook[]
+:see-git-hook:
+endif::git-hook[]
+ifndef::git-hook[]
+:see-git-hook: See linkgit:git-hook[1].
+endif::git-hook[]
+
 hook.<friendly-name>.command::
 	The command to execute for `hook.<friendly-name>`. `<friendly-name>`
 	is a unique name that identifies this hook. The hook events that
 	trigger the command are configured with `hook.<friendly-name>.event`.
 	The value can be an executable path or a shell oneliner. If more than
 	one value is specified for the same `<friendly-name>`, only the last
-	value parsed is used. See linkgit:git-hook[1].
+	value parsed is used. {see-git-hook}
 
 hook.<friendly-name>.event::
 	The hook events that trigger `hook.<friendly-name>`. The value is the
@@ -14,11 +21,87 @@
 	This is a multi-valued key. To run `hook.<friendly-name>` on multiple
 	events, specify the key more than once. An empty value resets
 	the list of events, clearing any previously defined events for
-	`hook.<friendly-name>`. See linkgit:git-hook[1].
+	`hook.<friendly-name>`. {see-git-hook}
++
+The `<friendly-name>` must not be the same as a known hook event name
+(e.g. do not use `hook.pre-commit.event`). Using a known event name as
+a friendly-name is a fatal error because it creates an ambiguity with
+`hook.<event>.enabled` and `hook.<event>.jobs`. For unknown event names,
+a warning is issued when `<friendly-name>` matches the event value.
 
 hook.<friendly-name>.enabled::
 	Whether the hook `hook.<friendly-name>` is enabled. Defaults to `true`.
 	Set to `false` to disable the hook without removing its
 	configuration. This is particularly useful when a hook is defined
 	in a system or global config file and needs to be disabled for a
-	specific repository. See linkgit:git-hook[1].
+	specific repository. {see-git-hook}
+
+hook.<friendly-name>.parallel::
+	Whether the hook `hook.<friendly-name>` may run in parallel with other hooks
+	for the same event. Defaults to `false`. Set to `true` only when the
+	hook script is safe to run concurrently with other hooks for the same
+	event. If any hook for an event does not have this set to `true`,
+	all hooks for that event run sequentially regardless of `hook.jobs`.
+	Only configured (named) hooks need to declare this. Traditional hooks
+	found in the hooks directory do not need to, and run in parallel when
+	the effective job count is greater than 1. {see-git-hook}
+
+hook.<event>.enabled::
+	Switch to enable or disable all hooks for the `<event>` hook event.
+	When set to `false`, no hooks fire for that event, regardless of any
+	per-hook `hook.<friendly-name>.enabled` settings. Defaults to `true`.
+	{see-git-hook}
++
+Note on naming: `<event>` must be the event name (e.g. `pre-commit`),
+not a hook friendly-name. Since using a known event name as a
+friendly-name is disallowed (see `hook.<friendly-name>.event` above),
+there is no ambiguity between event-level and per-hook `.enabled`
+settings for known events. For unknown events, if a friendly-name
+matches the event name despite the warning, `.enabled` is treated
+as per-hook only.
+
+hook.<event>.jobs::
+	Specifies how many hooks can be run simultaneously for the `<event>`
+	hook event (e.g. `hook.post-receive.jobs = 4`). Overrides `hook.jobs`
+	for this specific event. The same parallelism restrictions apply: this
+	setting has no effect unless all configured hooks for the event have
+	`hook.<friendly-name>.parallel` set to `true`. Set to `-1` to use the
+	number of available CPU cores. Must be a positive integer or `-1`;
+	zero is rejected with a warning. {see-git-hook}
++
+Note on naming: although this key resembles `hook.<friendly-name>.*`
+(a per-hook setting), `<event>` must be the event name, not a hook
+friendly name. The key component is stored literally and looked up by
+event name at runtime with no translation between the two namespaces.
+A key like `hook.my-hook.jobs` is stored under `"my-hook"` but the
+lookup at runtime uses the event name (e.g. `"post-receive"`), so
+`hook.my-hook.jobs` is silently ignored even when `my-hook` is
+registered for that event. Use `hook.post-receive.jobs` or any other
+valid event name when setting `hook.<event>.jobs`.
+
+hook.jobs::
+	Specifies how many hooks can be run simultaneously during parallelized
+	hook execution. If unspecified, defaults to 1 (serial execution).
+	Set to `-1` to use the number of available CPU cores.
+	Can be overridden on a per-event basis with `hook.<event>.jobs`.
+	Some hooks always run sequentially regardless of this setting because
+	they operate on shared data and cannot safely be parallelized:
++
+--
+`applypatch-msg`;;
+`prepare-commit-msg`;;
+`commit-msg`;;
+	Receive a commit message file and may rewrite it in place.
+`pre-commit`;;
+`post-checkout`;;
+`push-to-checkout`;;
+`post-commit`;;
+	Access the working tree, index, or repository state.
+--
++
+This setting has no effect unless all configured hooks for the event have
+`hook.<friendly-name>.parallel` set to `true`.
++
+For `pre-push` hooks, which normally keep stdout and stderr separate,
+setting this to a value greater than 1 (or passing `-j`) will merge stdout
+into stderr to allow correct de-interleaving of parallel output.
diff --git a/Documentation/config/http.adoc b/Documentation/config/http.adoc
index 849c89f..792a71b 100644
--- a/Documentation/config/http.adoc
+++ b/Documentation/config/http.adoc
@@ -59,7 +59,18 @@
 	Attempt authentication without seeking a username or password.  This
 	can be used to attempt GSS-Negotiate authentication without specifying
 	a username in the URL, as libcurl normally requires a username for
-	authentication.
+	authentication. Possible values are:
++
+--
+* `auto` (default) - Send empty credentials only if the server's 401 response
+  advertises an authentication mechanism that requires them (such as
+  GSS-Negotiate); otherwise fall back to prompting via the credential helper.
+* `true` - Always send empty credentials on the very first request, before
+  receiving any 401 response from the server.
+* `false` - Never send empty credentials. Mechanisms that require
+  empty credentials or an explicit username, such as GSS-Negotiate, will not
+  work.
+--
 
 http.proactiveAuth::
 	Attempt authentication without first making an unauthenticated attempt and
diff --git a/Documentation/config/imap.adoc b/Documentation/config/imap.adoc
index 4682a6b..cb8f5e2 100644
--- a/Documentation/config/imap.adoc
+++ b/Documentation/config/imap.adoc
@@ -1,44 +1,44 @@
-imap.folder::
+`imap.folder`::
 	The folder to drop the mails into, which is typically the Drafts
 	folder. For example: `INBOX.Drafts`, `INBOX/Drafts` or
 	`[Gmail]/Drafts`. The IMAP folder to interact with MUST be specified;
 	the value of this configuration variable is used as the fallback
 	default value when the `--folder` option is not given.
 
-imap.tunnel::
+`imap.tunnel`::
 	Command used to set up a tunnel to the IMAP server through which
 	commands will be piped instead of using a direct network connection
-	to the server. Required when imap.host is not set.
+	to the server. Required when `imap.host` is not set.
 
-imap.host::
+`imap.host`::
 	A URL identifying the server. Use an `imap://` prefix for non-secure
 	connections and an `imaps://` prefix for secure connections.
-	Ignored when imap.tunnel is set, but required otherwise.
+	Ignored when `imap.tunnel` is set, but required otherwise.
 
-imap.user::
+`imap.user`::
 	The username to use when logging in to the server.
 
-imap.pass::
+`imap.pass`::
 	The password to use when logging in to the server.
 
-imap.port::
+`imap.port`::
 	An integer port number to connect to on the server.
-	Defaults to 143 for imap:// hosts and 993 for imaps:// hosts.
-	Ignored when imap.tunnel is set.
+	Defaults to 143 for `imap://` hosts and 993 for `imaps://` hosts.
+	Ignored when `imap.tunnel` is set.
 
-imap.sslverify::
+`imap.sslverify`::
 	A boolean to enable/disable verification of the server certificate
 	used by the SSL/TLS connection. Default is `true`. Ignored when
-	imap.tunnel is set.
+	`imap.tunnel` is set.
 
-imap.preformattedHTML::
+`imap.preformattedHTML`::
 	A boolean to enable/disable the use of html encoding when sending
-	a patch.  An html encoded patch will be bracketed with <pre>
+	a patch.  An html encoded patch will be bracketed with `<pre>`
 	and have a content type of text/html.  Ironically, enabling this
 	option causes Thunderbird to send the patch as a plain/text,
 	format=fixed email.  Default is `false`.
 
-imap.authMethod::
+`imap.authMethod`::
 	Specify the authentication method for authenticating with the IMAP server.
 	If Git was built with the NO_CURL option, or if your curl version is older
 	than 7.34.0, or if you're running git-imap-send with the `--no-curl`
diff --git a/Documentation/config/mergetool.adoc b/Documentation/config/mergetool.adoc
index 7064f5a..7afdcad 100644
--- a/Documentation/config/mergetool.adoc
+++ b/Documentation/config/mergetool.adoc
@@ -52,13 +52,13 @@
 	if `merge.tool` is configured as _<variant>_), Git will consult
 	`mergetool.<variant>.layout` to determine the tool's layout. If the
 	variant-specific configuration is not available, `vimdiff` ' s is used as
-	fallback.  If that too is not available, a default layout with 4 windows
-	will be used.  To configure the layout, see the 'BACKEND SPECIFIC HINTS'
+	fallback. If that too is not available, a default layout with 4 windows
+	will be used.
 ifdef::git-mergetool[]
-	section.
+To configure the layout, see the 'BACKEND SPECIFIC HINTS' section.
 endif::[]
 ifndef::git-mergetool[]
-	section in linkgit:git-mergetool[1].
+To configure the layout, see the 'BACKEND SPECIFIC HINTS' section in linkgit:git-mergetool[1].
 endif::[]
 
 `mergetool.hideResolved`::
diff --git a/Documentation/config/pack.adoc b/Documentation/config/pack.adoc
index fa997c8..22384c2 100644
--- a/Documentation/config/pack.adoc
+++ b/Documentation/config/pack.adoc
@@ -161,7 +161,7 @@
 
 pack.preferBitmapTips::
 	Specifies a ref hierarchy (e.g., "refs/heads/"); can be
-	given multiple times to specify more than one hierarchies.
+	given multiple times to specify more than one hierarchy.
 	When selecting which commits will receive bitmaps, prefer a
 	commit at the tip of a reference that is contained in any of
 	the configured hierarchies.
diff --git a/Documentation/config/remote.adoc b/Documentation/config/remote.adoc
index 91e46f6..eb9c8a3 100644
--- a/Documentation/config/remote.adoc
+++ b/Documentation/config/remote.adoc
@@ -107,6 +107,55 @@
 the values inherited from a lower priority configuration files (e.g.
 `$HOME/.gitconfig`).
 
+remote.<name>.negotiationRestrict::
+	When negotiating with this remote during `git fetch`, restrict the
+	commits advertised as "have" lines to only those reachable from refs
+	matching the given patterns.  This multi-valued config option behaves
+	like `--negotiation-restrict` on the command line.
++
+Each value is either an exact ref name (e.g. `refs/heads/release`) or a
+glob pattern (e.g. `refs/heads/release/*`).  The pattern syntax is the
+same as for `--negotiation-restrict`.
++
+These config values are used as defaults for the `--negotiation-restrict`
+command-line option.  If `--negotiation-restrict` (or its synonym
+`--negotiation-tip`) is specified on the command line, then the config
+values are not used.
++
+These values also influence negotiation during `git push` if
+`push.negotiate` is enabled.
++
+Blank values signal to ignore all previous values, allowing a reset of
+the list from broader config scenarios.
+
+remote.<name>.negotiationInclude::
+	When negotiating with this remote during `git fetch`, the client
+	advertises a list of commits that exist locally.  In repos with
+	many references, this list of "haves" can be truncated. Depending
+	on data shape, dropping certain references may be expensive. This
+	multi-valued config option specifies references, commit hashes,
+	or ref pattern globs whose tips should always be sent as "have"
+	commits during fetch negotiation with this remote.
++
+Each value is either an exact ref name (e.g. `refs/heads/release`), a
+commit hash, or a glob pattern (e.g. `refs/heads/release/*`).  The
+pattern syntax is the same as for `--negotiation-include`.
++
+These config values are used as defaults for the `--negotiation-include`
+command-line option.  If `--negotiation-include` is specified on the
+command line, then the config values are not used.
++
+This option is additive with the normal negotiation process: the
+negotiation algorithm still runs and advertises its own selected commits,
+but the refs matching `remote.<name>.negotiationInclude` are sent
+unconditionally on top of those heuristically selected commits.
++
+These values also influence negotiation during `git push` if
+`push.negotiate` is enabled.
++
+Blank values signal to ignore all previous values, allowing a reset of
+the list from broader config scenarios.
+
 remote.<name>.followRemoteHEAD::
 	How linkgit:git-fetch[1] should handle updates to `remotes/<name>/HEAD`
 	when fetching using the configured refspecs of a remote.
diff --git a/Documentation/config/repack.adoc b/Documentation/config/repack.adoc
index e9e78dc..4c22a49 100644
--- a/Documentation/config/repack.adoc
+++ b/Documentation/config/repack.adoc
@@ -46,3 +46,21 @@
 	`--write-midx`. When false, cruft packs are only included in the MIDX
 	when necessary (e.g., because they might be required to form a
 	reachability closure with MIDX bitmaps). Defaults to true.
+
+repack.midxSplitFactor::
+	The factor used in the geometric merging condition when
+	compacting incremental MIDX layers during `git repack` when
+	invoked with the `--write-midx=incremental` option.
++
+Adjacent layers are merged when the accumulated object count of the
+newer layer exceeds `1/<N>` of the object count of the next deeper
+layer. Must be at least 2. Defaults to 2.
+
+repack.midxNewLayerThreshold::
+	The minimum number of packs in the tip MIDX layer before those
+	packs are considered as candidates for geometric repacking
+	during `git repack --write-midx=incremental`.
++
+When the tip layer has fewer packs than this threshold, those packs are
+excluded from the geometric repack entirely, and are thus left
+unmodified. Must be at least 1. Defaults to 8.
diff --git a/Documentation/config/safe.adoc b/Documentation/config/safe.adoc
index 2d45c98..5b1690a 100644
--- a/Documentation/config/safe.adoc
+++ b/Documentation/config/safe.adoc
@@ -2,10 +2,12 @@
 	Specifies which bare repositories Git will work with. The currently
 	supported values are:
 +
-* `all`: Git works with all bare repositories. This is the default.
+* `all`: Git works with all bare repositories. This is the default in
+  Git 2.x.
 * `explicit`: Git only works with bare repositories specified via
   the top-level `--git-dir` command-line option, or the `GIT_DIR`
-  environment variable (see linkgit:git[1]).
+  environment variable (see linkgit:git[1]). This will be the default
+  in Git 3.0.
 +
 If you do not use bare repositories in your workflow, then it may be
 beneficial to set `safe.bareRepository` to `explicit` in your global
@@ -13,6 +15,10 @@
 repository that contains a bare repository and running a Git command
 within that directory.
 +
+If you use bare repositories regularly and want to preserve the current
+behavior after upgrading to Git 3.0, set `safe.bareRepository` to `all`
+in your global or system config.
++
 This config setting is only respected in protected configuration (see
 <<SCOPES>>). This prevents untrusted repositories from tampering with
 this value.
diff --git a/Documentation/config/sendemail.adoc b/Documentation/config/sendemail.adoc
index 6560ecc..1d70055 100644
--- a/Documentation/config/sendemail.adoc
+++ b/Documentation/config/sendemail.adoc
@@ -22,7 +22,7 @@
 sendemail.smtpSSLClientKey::
 	Path to the client private key file that corresponds to the client
 	certificate. To avoid misconfiguration, this configuration must be used
-	in conjunction with `sendemail.smtpSSLClientKey` or the
+	in conjunction with `sendemail.smtpSSLClientCert` or the
 	`--smtp-ssl-client-cert` option. If the client key is included in the
 	client certificate, the choice of private key depends on the format of
 	the certificate. Visit https://metacpan.org/pod/IO::Socket::SSL for more
diff --git a/Documentation/config/sideband.adoc b/Documentation/config/sideband.adoc
new file mode 100644
index 0000000..96fade7
--- /dev/null
+++ b/Documentation/config/sideband.adoc
@@ -0,0 +1,27 @@
+sideband.allowControlCharacters::
+	By default, control characters that are delivered via the sideband
+	are masked, except ANSI color sequences. This prevents potentially
+	unwanted ANSI escape sequences from being sent to the terminal. Use
+	this config setting to override this behavior (the value can be
+	a comma-separated list of the following keywords):
++
+--
+	`color`::
+		Allow ANSI color sequences, line feeds and horizontal tabs,
+		but mask all other control characters. This is the default.
+	`cursor:`:
+		Allow control sequences that move the cursor. This is
+		disabled by default.
+	`erase`::
+		Allow control sequences that erase charactrs. This is
+		disabled by default.
+	`false`::
+		Mask all control characters other than line feeds and
+		horizontal tabs.
+	`true`::
+		Allow all control characters to be sent to the terminal.
+--
+
+sideband.<url>.*::
+	Apply the `sideband.*` option selectively to specific URLs. The
+	same URL matching logic applies as for `http.<url>.*` settings.
diff --git a/Documentation/config/submodule.adoc b/Documentation/config/submodule.adoc
index 8dacb85..4c42401 100644
--- a/Documentation/config/submodule.adoc
+++ b/Documentation/config/submodule.adoc
@@ -40,7 +40,7 @@
 	takes only differences between the HEAD of the submodule and the commit
 	recorded in the superproject into account. "untracked" will additionally
 	let submodules with modified tracked files in their work tree show up.
-	When set to "none"(default) It also show submodules as changed if they have
+	When set to "none" (default) it also shows submodules as changed if they have
 	untracked files in their work tree.
 	This setting overrides any setting made in .gitmodules for this submodule,
 	both settings can be overridden on the command line by using the
diff --git a/Documentation/diff-format.adoc b/Documentation/diff-format.adoc
index 9f7e988..ef5df14 100644
--- a/Documentation/diff-format.adoc
+++ b/Documentation/diff-format.adoc
@@ -19,9 +19,9 @@
 `git-diff-files [<pattern>...]`::
         compares the index and the files on the filesystem.
 
-The `git-diff-tree` command begins its output by printing the hash of
-what is being compared. After that, all the commands print one output
-line per changed file.
+All the commands print one output line per changed file,
+except `git diff-files` in the case of an unmerged file, which prints
+both an "unmerged" and an "in-place edit" line.
 
 An output line is formatted this way:
 
@@ -37,13 +37,13 @@
 That is, from the left to the right:
 
 . a colon.
-. mode for "src"; 000000 if creation or unmerged.
+. mode for "src"; 000000 if creation, or if "src" is from the index and is unmerged.
 . a space.
-. mode for "dst"; 000000 if deletion or unmerged.
+. mode for "dst"; 000000 if deletion, or if "dst" is from the index and is unmerged.
 . a space.
-. sha1 for "src"; 0\{40\} if creation or unmerged.
+. sha1 for "src"; 0\{40\} if creation, or if "src" is from the index and is unmerged.
 . a space.
-. sha1 for "dst"; 0\{40\} if deletion, unmerged or "work tree out of sync with the index".
+. sha1 for "dst"; 0\{40\} if deletion, if "dst" is from the index and is unmerged, or if "dst" is from the work tree and is out of sync with the index.
 . a space.
 . status, followed by optional "score" number.
 . a tab or a NUL when `-z` option is used.
diff --git a/Documentation/fetch-options.adoc b/Documentation/fetch-options.adoc
index 81a9d7f..8074004 100644
--- a/Documentation/fetch-options.adoc
+++ b/Documentation/fetch-options.adoc
@@ -49,6 +49,7 @@
 	`.git/shallow`. This option updates `.git/shallow` and accepts such
 	refs.
 
+`--negotiation-restrict=(<commit>|<glob>)`::
 `--negotiation-tip=(<commit>|<glob>)`::
 	By default, Git will report, to the server, commits reachable
 	from all local refs to find common commits in an attempt to
@@ -58,6 +59,9 @@
 	local ref is likely to have commits in common with the
 	upstream ref being fetched.
 +
+`--negotiation-restrict` is the preferred name for this option;
+`--negotiation-tip` is accepted as a synonym.
++
 This option may be specified more than once; if so, Git will report
 commits reachable from any of the given commits.
 +
@@ -69,9 +73,32 @@
 configuration variables documented in linkgit:git-config[1], and the
 `--negotiate-only` option below.
 
+`--negotiation-include=(<commit>|<glob>)`::
+	Ensure that the commits at the given tips are always sent as "have"
+	lines during fetch negotiation, regardless of what the negotiation
+	algorithm selects.  This is useful to guarantee that common
+	history reachable from specific refs is always considered, even
+	when `--negotiation-restrict` restricts the set of tips or when
+	the negotiation algorithm would otherwise skip them.
++
+This option may be specified more than once; if so, each commit is sent
+unconditionally.
++
+The argument may be an exact ref name (e.g. `refs/heads/release`), an
+object hash, or a glob pattern (e.g. `refs/heads/release/{asterisk}`).
+The pattern syntax is the same as for `--negotiation-restrict`.
++
+If `--negotiation-restrict` is used, the have set is first restricted by
+that option and then increased to include the tips specified by
+`--negotiation-include`.
++
+If this option is not specified on the command line, then any
+`remote.<name>.negotiationInclude` config values for the current remote
+are used instead.
+
 `--negotiate-only`::
 	Do not fetch anything from the server, and instead print the
-	ancestors of the provided `--negotiation-tip=` arguments,
+	ancestors of the provided `--negotiation-restrict=` arguments,
 	which we have in common with the server.
 +
 This is incompatible with `--recurse-submodules=(yes|on-demand)`.
diff --git a/Documentation/format-patch-caveats.adoc b/Documentation/format-patch-caveats.adoc
index 807a65b..133e475 100644
--- a/Documentation/format-patch-caveats.adoc
+++ b/Documentation/format-patch-caveats.adoc
@@ -28,6 +28,6 @@
 email workflows.
 
 Given these limitations, one might be tempted to use a general-purpose
-utility like patch(1) instead. However, patch(1) will not only look for
+utility like `patch`(1) instead. However, `patch`(1) will not only look for
 unindented diffs (like linkgit:git-am[1]) but will try to apply indented
 diffs as well.
diff --git a/Documentation/format-patch-end-of-commit-message.adoc b/Documentation/format-patch-end-of-commit-message.adoc
index ec1ef79..a1a624d 100644
--- a/Documentation/format-patch-end-of-commit-message.adoc
+++ b/Documentation/format-patch-end-of-commit-message.adoc
@@ -1,8 +1,8 @@
 Any line that is of the form:
 
 * three-dashes and end-of-line, or
-* a line that begins with "diff -", or
-* a line that begins with "Index: "
+* a line that begins with `diff -`, or
+* a line that begins with `Index: `
 
 is taken as the beginning of a patch, and the commit log message
 is terminated before the first occurrence of such a line.
diff --git a/Documentation/git-am.adoc b/Documentation/git-am.adoc
index 384e0cd..28adf4c 100644
--- a/Documentation/git-am.adoc
+++ b/Documentation/git-am.adoc
@@ -8,17 +8,17 @@
 
 SYNOPSIS
 --------
-[verse]
-'git am' [--signoff] [--keep] [--[no-]keep-cr] [--[no-]utf8] [--[no-]verify]
+[synopsis]
+git am [--signoff] [--keep] [--[no-]keep-cr] [--[no-]utf8] [--[no-]verify]
 	 [--[no-]3way] [--interactive] [--committer-date-is-author-date]
 	 [--ignore-date] [--ignore-space-change | --ignore-whitespace]
 	 [--whitespace=<action>] [-C<n>] [-p<n>] [--directory=<dir>]
 	 [--exclude=<path>] [--include=<path>] [--reject] [-q | --quiet]
-	 [--[no-]scissors] [-S[<keyid>]] [--patch-format=<format>]
+	 [--[no-]scissors] [-S[<key-id>]] [--patch-format=<format>]
 	 [--quoted-cr=<action>]
 	 [--empty=(stop|drop|keep)]
 	 [(<mbox> | <Maildir>)...]
-'git am' (--continue | --skip | --abort | --quit | --retry | --show-current-patch[=(diff|raw)] | --allow-empty)
+git am (--continue | --skip | --abort | --quit | --retry | --show-current-patch[=(diff|raw)] | --allow-empty)
 
 DESCRIPTION
 -----------
@@ -30,45 +30,45 @@
 
 OPTIONS
 -------
-(<mbox>|<Maildir>)...::
+`(<mbox>|<Maildir>)...`::
 	The list of mailbox files to read patches from. If you do not
 	supply this argument, the command reads from the standard input.
 	If you supply directories, they will be treated as Maildirs.
 
--s::
---signoff::
+`-s`::
+`--signoff`::
 	Add a `Signed-off-by` trailer to the commit message (see
 	linkgit:git-interpret-trailers[1]), using the committer identity
 	of yourself.  See the signoff option in linkgit:git-commit[1]
 	for more information.
 
--k::
---keep::
+`-k`::
+`--keep`::
 	Pass `-k` flag to linkgit:git-mailinfo[1].
 
---keep-non-patch::
+`--keep-non-patch`::
 	Pass `-b` flag to linkgit:git-mailinfo[1].
 
---keep-cr::
---no-keep-cr::
+`--keep-cr`::
+`--no-keep-cr`::
 	With `--keep-cr`, call linkgit:git-mailsplit[1]
 	with the same option, to prevent it from stripping CR at the end of
 	lines. `am.keepcr` configuration variable can be used to specify the
 	default behaviour.  `--no-keep-cr` is useful to override `am.keepcr`.
 
--c::
---scissors::
+`-c`::
+`--scissors`::
 	Remove everything in body before a scissors line (see
 	linkgit:git-mailinfo[1]). Can be activated by default using
 	the `mailinfo.scissors` configuration variable.
 
---no-scissors::
+`--no-scissors`::
 	Ignore scissors lines (see linkgit:git-mailinfo[1]).
 
---quoted-cr=<action>::
+`--quoted-cr=<action>`::
 	This flag will be passed down to linkgit:git-mailinfo[1].
 
---empty=(drop|keep|stop)::
+`--empty=(drop|keep|stop)`::
 	How to handle an e-mail message lacking a patch:
 +
 --
@@ -82,24 +82,23 @@
 	session. This is the default behavior.
 --
 
--m::
---message-id::
-	Pass the `-m` flag to linkgit:git-mailinfo[1], so that the
-	`Message-ID` header is added as a trailer (see
-	linkgit:git-interpret-trailers[1]).  The `am.messageid`
-	configuration variable can be used to specify the default
-	behaviour.
+`-m`::
+`--message-id`::
+	Pass the `-m` flag to linkgit:git-mailinfo[1],
+	so that the `Message-ID` header is added to the commit message.
+	The `am.messageid` configuration variable can be used to specify
+	the default behaviour.
 
---no-message-id::
+`--no-message-id`::
 	Do not add the Message-ID header to the commit message.
-	`no-message-id` is useful to override `am.messageid`.
+	`--no-message-id` is useful to override `am.messageid`.
 
--q::
---quiet::
+`-q`::
+`--quiet`::
 	Be quiet. Only print error messages.
 
--u::
---utf8::
+`-u`::
+`--utf8`::
 	Pass `-u` flag to linkgit:git-mailinfo[1].
 	The proposed commit log message taken from the e-mail
 	is re-coded into UTF-8 encoding (configuration variable
@@ -109,57 +108,57 @@
 This was optional in prior versions of git, but now it is the
 default.   You can use `--no-utf8` to override this.
 
---no-utf8::
+`--no-utf8`::
 	Pass `-n` flag to linkgit:git-mailinfo[1].
 
--3::
---3way::
---no-3way::
+`-3`::
+`--3way`::
+`--no-3way`::
 	When the patch does not apply cleanly, fall back on
 	3-way merge if the patch records the identity of blobs
 	it is supposed to apply to and we have those blobs
 	available locally. `--no-3way` can be used to override
-	am.threeWay configuration variable. For more information,
-	see am.threeWay in linkgit:git-config[1].
+	`am.threeWay` configuration variable. For more information,
+	see `am.threeWay` in linkgit:git-config[1].
 
 include::rerere-options.adoc[]
 
---ignore-space-change::
---ignore-whitespace::
---whitespace=<action>::
--C<n>::
--p<n>::
---directory=<dir>::
---exclude=<path>::
---include=<path>::
---reject::
+`--ignore-space-change`::
+`--ignore-whitespace`::
+`--whitespace=<action>`::
+`-C<n>`::
+`-p<n>`::
+`--directory=<dir>`::
+`--exclude=<path>`::
+`--include=<path>`::
+`--reject`::
 	These flags are passed to the linkgit:git-apply[1] program that
 	applies the patch.
 +
-Valid <action> for the `--whitespace` option are:
+Valid _<action>_ for the `--whitespace` option are:
 `nowarn`, `warn`, `fix`, `error`, and `error-all`.
 
---patch-format::
+`--patch-format`::
 	By default the command will try to detect the patch format
 	automatically. This option allows the user to bypass the automatic
 	detection and specify the patch format that the patch(es) should be
 	interpreted as. Valid formats are mbox, mboxrd,
 	stgit, stgit-series, and hg.
 
--i::
---interactive::
+`-i`::
+`--interactive`::
 	Run interactively.
 
---verify::
--n::
---no-verify::
+`--verify`::
+`-n`::
+`--no-verify`::
 	Run the `pre-applypatch` and `applypatch-msg` hooks. This is the
 	default. Skip these hooks with `-n` or `--no-verify`. See also
 	linkgit:githooks[5].
 +
 Note that `post-applypatch` cannot be skipped.
 
---committer-date-is-author-date::
+`--committer-date-is-author-date`::
 	By default the command records the date from the e-mail
 	message as the commit author date, and uses the time of
 	commit creation as the committer date. This allows the
@@ -173,29 +172,29 @@
 older (in terms of the commit date) than the oldest patch you are
 applying.
 
---ignore-date::
+`--ignore-date`::
 	By default the command records the date from the e-mail
 	message as the commit author date, and uses the time of
 	commit creation as the committer date. This allows the
 	user to lie about the author date by using the same
 	value as the committer date.
 
---skip::
+`--skip`::
 	Skip the current patch.  This is only meaningful when
 	restarting an aborted patch.
 
--S[<keyid>]::
---gpg-sign[=<keyid>]::
---no-gpg-sign::
-	GPG-sign commits. The `keyid` argument is optional and
+`-S[<key-id>]`::
+`--gpg-sign[=<key-id>]`::
+`--no-gpg-sign`::
+	GPG-sign commits. The _<key-id>_ is optional and
 	defaults to the committer identity; if specified, it must be
 	stuck to the option without a space. `--no-gpg-sign` is useful to
 	countermand both `commit.gpgSign` configuration variable, and
 	earlier `--gpg-sign`.
 
---continue::
--r::
---resolved::
+`--continue`::
+`-r`::
+`--resolved`::
 	After a patch failure (e.g. attempting to apply
 	conflicting patch), the user has applied it by hand and
 	the index file stores the result of the application.
@@ -203,36 +202,36 @@
 	extracted from the e-mail message and the current index
 	file, and continue.
 
---resolvemsg=<msg>::
-	When a patch failure occurs, <msg> will be printed
+`--resolvemsg=<msg>`::
+	When a patch failure occurs, _<msg>_ will be printed
 	to the screen before exiting.  This overrides the
 	standard message informing you to use `--continue`
 	or `--skip` to handle the failure.  This is solely
 	for internal use between linkgit:git-rebase[1] and
 	linkgit:git-am[1].
 
---abort::
+`--abort`::
 	Restore the original branch and abort the patching operation.
 	Revert the contents of files involved in the am operation to their
 	pre-am state.
 
---quit::
-	Abort the patching operation but keep HEAD and the index
+`--quit`::
+	Abort the patching operation but keep `HEAD` and the index
 	untouched.
 
---retry::
+`--retry`::
 	Try to apply the last conflicting patch again. This is generally
 	only useful for passing extra options to the retry attempt
 	(e.g., `--3way`), since otherwise you'll just see the same
 	failure again.
 
---show-current-patch[=(diff|raw)]::
+`--show-current-patch[=(diff|raw)]`::
 	Show the message at which linkgit:git-am[1] has stopped due to
 	conflicts.  If `raw` is specified, show the raw contents of
 	the e-mail message; if `diff`, show the diff portion only.
 	Defaults to `raw`.
 
---allow-empty::
+`--allow-empty`::
 	After a patch failure on an input e-mail message lacking a patch,
 	create an empty commit with the contents of the e-mail message
 	as its log message.
@@ -279,11 +278,11 @@
 run `git am --abort` before running the command with mailbox
 names.
 
-Before any patches are applied, ORIG_HEAD is set to the tip of the
+Before any patches are applied, `ORIG_HEAD` is set to the tip of the
 current branch.  This is useful if you have problems with multiple
 commits, like running linkgit:git-am[1] on the wrong branch or an error
 in the commits that is more easily fixed by changing the mailbox (e.g.
-errors in the "From:" lines).
+errors in the `From:` lines).
 
 [[caveats]]
 CAVEATS
diff --git a/Documentation/git-apply.adoc b/Documentation/git-apply.adoc
index 6c71ee6..3f22dac 100644
--- a/Documentation/git-apply.adoc
+++ b/Documentation/git-apply.adoc
@@ -8,8 +8,8 @@
 
 SYNOPSIS
 --------
-[verse]
-'git apply' [--stat] [--numstat] [--summary] [--check]
+[synopsis]
+git apply [--stat] [--numstat] [--summary] [--check]
 	  [--index | --intent-to-add] [--3way] [--ours | --theirs | --union]
 	  [--apply] [--no-add] [--build-fake-ancestor=<file>] [-R | --reverse]
 	  [--allow-binary-replacement | --binary] [--reject] [-z]
@@ -35,33 +35,33 @@
 
 OPTIONS
 -------
-<patch>...::
-	The files to read the patch from.  '-' can be used to read
+`<patch>...`::
+	The files to read the patch from.  `-` can be used to read
 	from the standard input.
 
---stat::
+`--stat`::
 	Instead of applying the patch, output diffstat for the
 	input.  Turns off "apply".
 
---numstat::
+`--numstat`::
 	Similar to `--stat`, but shows the number of added and
 	deleted lines in decimal notation and the pathname without
 	abbreviation, to make it more machine friendly.  For
 	binary files, outputs two `-` instead of saying
 	`0 0`.  Turns off "apply".
 
---summary::
+`--summary`::
 	Instead of applying the patch, output a condensed
 	summary of information obtained from git diff extended
 	headers, such as creations, renames, and mode changes.
 	Turns off "apply".
 
---check::
+`--check`::
 	Instead of applying the patch, see if the patch is
 	applicable to the current working tree and/or the index
 	file and detects errors.  Turns off "apply".
 
---index::
+`--index`::
 	Apply the patch to both the index and the working tree (or
 	merely check that it would apply cleanly to both if `--check` is
 	in effect). Note that `--index` expects index entries and
@@ -70,13 +70,13 @@
 	raise an error if they are not, even if the patch would apply
 	cleanly to both the index and the working tree in isolation.
 
---cached::
+`--cached`::
 	Apply the patch to just the index, without touching the working
 	tree. If `--check` is in effect, merely check that it would
 	apply cleanly to the index entry.
 
--N::
---intent-to-add::
+`-N`::
+`--intent-to-add`::
 	When applying the patch only to the working tree, mark new
 	files to be added to the index later (see `--intent-to-add`
 	option in linkgit:git-add[1]). This option is ignored if
@@ -84,8 +84,8 @@
 	repository. Note that `--index` could be implied by other options
 	such as `--3way`.
 
--3::
---3way::
+`-3`::
+`--3way`::
 	Attempt 3-way merge if the patch records the identity of blobs it is supposed
 	to apply to and we have those blobs available locally, possibly leaving the
 	conflict markers in the files in the working tree for the user to
@@ -94,14 +94,14 @@
 	When used with the `--cached` option, any conflicts are left at higher stages
 	in the cache.
 
---ours::
---theirs::
---union::
+`--ours`::
+`--theirs`::
+`--union`::
 	Instead of leaving conflicts in the file, resolve conflicts favouring
-	our (or their or both) side of the lines. Requires --3way.
+	our (or their or both) side of the lines. Requires `--3way`.
 
---build-fake-ancestor=<file>::
-	Newer 'git diff' output has embedded 'index information'
+`--build-fake-ancestor=<file>`::
+	Newer `git diff` output has embedded 'index information'
 	for each blob to help identify the original version that
 	the patch applies to.  When this flag is given, and if
 	the original versions of the blobs are available locally,
@@ -110,18 +110,18 @@
 When a pure mode change is encountered (which has no index information),
 the information is read from the current index instead.
 
--R::
---reverse::
+`-R`::
+`--reverse`::
 	Apply the patch in reverse.
 
---reject::
-	For atomicity, 'git apply' by default fails the whole patch and
+`--reject`::
+	For atomicity, `git apply` by default fails the whole patch and
 	does not touch the working tree when some of the hunks
 	do not apply.  This option makes it apply
 	the parts of the patch that are applicable, and leave the
-	rejected hunks in corresponding *.rej files.
+	rejected hunks in corresponding `*.rej` files.
 
--z::
+`-z`::
 	When `--numstat` has been given, do not munge pathnames,
 	but use a NUL-terminated machine-readable format.
 +
@@ -129,20 +129,20 @@
 explained for the configuration variable `core.quotePath` (see
 linkgit:git-config[1]).
 
--p<n>::
-	Remove <n> leading path components (separated by slashes) from
+`-p<n>`::
+	Remove _<n>_ leading path components (separated by slashes) from
 	traditional diff paths. E.g., with `-p2`, a patch against
 	`a/dir/file` will be applied directly to `file`. The default is
 	1.
 
--C<n>::
-	Ensure at least <n> lines of surrounding context match before
+`-C<n>`::
+	Ensure at least _<n>_ lines of surrounding context match before
 	and after each change.  When fewer lines of surrounding
 	context exist they all must match.  By default no context is
 	ever ignored.
 
---unidiff-zero::
-	By default, 'git apply' expects that the patch being
+`--unidiff-zero`::
+	By default, `git apply` expects that the patch being
 	applied is a unified diff with at least one line of context.
 	This provides good safety measures, but breaks down when
 	applying a diff generated with `--unified=0`. To bypass these
@@ -151,34 +151,34 @@
 Note, for the reasons stated above, the usage of context-free patches is
 discouraged.
 
---apply::
+`--apply`::
 	If you use any of the options marked "Turns off
-	'apply'" above, 'git apply' reads and outputs the
+	'apply'" above, `git apply` reads and outputs the
 	requested information without actually applying the
 	patch.  Give this flag after those flags to also apply
 	the patch.
 
---no-add::
+`--no-add`::
 	When applying a patch, ignore additions made by the
 	patch.  This can be used to extract the common part between
-	two files by first running 'diff' on them and applying
+	two files by first running `diff` on them and applying
 	the result with this option, which would apply the
 	deletion part but not the addition part.
 
---allow-binary-replacement::
---binary::
+`--allow-binary-replacement`::
+`--binary`::
 	Historically we did not allow binary patch application
 	without an explicit permission from the user, and this
 	flag was the way to do so.  Currently, we always allow binary
 	patch application, so this is a no-op.
 
---exclude=<path-pattern>::
-	Don't apply changes to files matching the given path pattern. This can
+`--exclude=<path-pattern>`::
+	Don't apply changes to files matching _<path-pattern>_. This can
 	be useful when importing patchsets, where you want to exclude certain
 	files or directories.
 
---include=<path-pattern>::
-	Apply changes to files matching the given path pattern. This can
+`--include=<path-pattern>`::
+	Apply changes to files matching the _<path-pattern>_. This can
 	be useful when importing patchsets, where you want to include certain
 	files or directories.
 +
@@ -188,15 +188,15 @@
 include/exclude pattern is used by default if there is no include pattern
 on the command line, and ignored if there is any include pattern.
 
---ignore-space-change::
---ignore-whitespace::
+`--ignore-space-change`::
+`--ignore-whitespace`::
 	When applying a patch, ignore changes in whitespace in context
 	lines if necessary.
 	Context lines will preserve their whitespace, and they will not
 	undergo whitespace fixing regardless of the value of the
 	`--whitespace` option. New lines will still be fixed, though.
 
---whitespace=<action>::
+`--whitespace=<action>`::
 	When applying a patch, detect a new or modified line that has
 	whitespace errors.  What are considered whitespace errors is
 	controlled by `core.whitespace` configuration.  By default,
@@ -209,7 +209,7 @@
 When `git-apply` is used for statistics and not applying a
 patch, it defaults to `nowarn`.
 +
-You can use different `<action>` values to control this
+You can use different _<action>_ values to control this
 behavior:
 +
 * `nowarn` turns off the trailing whitespace warning.
@@ -223,48 +223,48 @@
   to apply the patch.
 * `error-all` is similar to `error` but shows all errors.
 
---inaccurate-eof::
-	Under certain circumstances, some versions of 'diff' do not correctly
+`--inaccurate-eof`::
+	Under certain circumstances, some versions of `diff` do not correctly
 	detect a missing new-line at the end of the file. As a result, patches
-	created by such 'diff' programs do not record incomplete lines
+	created by such `diff` programs do not record incomplete lines
 	correctly. This option adds support for applying such patches by
 	working around this bug.
 
--v::
---verbose::
+`-v`::
+`--verbose`::
 	Report progress to stderr. By default, only a message about the
 	current patch being applied will be printed. This option will cause
 	additional information to be reported.
 
--q::
---quiet::
+`-q`::
+`--quiet`::
 	Suppress stderr output. Messages about patch status and progress
 	will not be printed.
 
---recount::
+`--recount`::
 	Do not trust the line counts in the hunk headers, but infer them
 	by inspecting the patch (e.g. after editing the patch without
 	adjusting the hunk headers appropriately).
 
---directory=<root>::
-	Prepend <root> to all filenames.  If a "-p" argument was also passed,
+`--directory=<root>`::
+	Prepend _<root>_ to all filenames.  If a `-p` argument was also passed,
 	it is applied before prepending the new root.
 +
 For example, a patch that talks about updating `a/git-gui.sh` to `b/git-gui.sh`
 can be applied to the file in the working tree `modules/git-gui/git-gui.sh` by
 running `git apply --directory=modules/git-gui`.
 
---unsafe-paths::
+`--unsafe-paths`::
 	By default, a patch that affects outside the working area
 	(either a Git controlled working tree, or the current working
-	directory when "git apply" is used as a replacement of GNU
-	patch) is rejected as a mistake (or a mischief).
+	directory when `git apply` is used as a replacement of GNU
+	`patch`) is rejected as a mistake (or a mischief).
 +
-When `git apply` is used as a "better GNU patch", the user can pass
+When `git apply` is used as a "better GNU `patch`", the user can pass
 the `--unsafe-paths` option to override this safety check.  This option
 has no effect when `--index` or `--cached` is in use.
 
---allow-empty::
+`--allow-empty`::
 	Don't return an error for patches containing no diff. This includes
 	empty patches and patches with commit text only.
 
@@ -273,11 +273,12 @@
 
 include::includes/cmd-config-section-all.adoc[]
 
+:git-apply: 1
 include::config/apply.adoc[]
 
 SUBMODULES
 ----------
-If the patch contains any changes to submodules then 'git apply'
+If the patch contains any changes to submodules then `git apply`
 treats these changes as follows.
 
 If `--index` is specified (explicitly or implicitly), then the submodule
diff --git a/Documentation/git-archive.adoc b/Documentation/git-archive.adoc
index a0e3fe7..086bade 100644
--- a/Documentation/git-archive.adoc
+++ b/Documentation/git-archive.adoc
@@ -54,6 +54,11 @@
 	Prepend <prefix>/ to paths in the archive.  Can be repeated; its
 	rightmost value is used for all tracked files.  See below which
 	value gets used by `--add-file`.
++
+The <prefix> is used as given and is not normalized. It may
+include leading slashes or parent directory components (e.g.,
+`../`). Some archive consumers may treat such paths as
+potentially unsafe and adjust or warn during extraction.
 
 -o <file>::
 --output=<file>::
diff --git a/Documentation/git-backfill.adoc b/Documentation/git-backfill.adoc
index 246ab41..82d6a19 100644
--- a/Documentation/git-backfill.adoc
+++ b/Documentation/git-backfill.adoc
@@ -9,7 +9,7 @@
 SYNOPSIS
 --------
 [synopsis]
-git backfill [--min-batch-size=<n>] [--[no-]sparse]
+git backfill [--min-batch-size=<n>] [--[no-]sparse] [--[no-]include-edges] [<revision-range>]
 
 DESCRIPTION
 -----------
@@ -43,7 +43,7 @@
 time.
 
 By default, `git backfill` downloads all blobs reachable from the `HEAD`
-commit. This set can be restricted or expanded using various options.
+commit. This set can be restricted or expanded using various options below.
 
 THIS COMMAND IS EXPERIMENTAL. ITS BEHAVIOR MAY CHANGE IN THE FUTURE.
 
@@ -63,7 +63,27 @@
 	current sparse-checkout. If the sparse-checkout feature is enabled,
 	then `--sparse` is assumed and can be disabled with `--no-sparse`.
 
-You may also specify the commit limiting options from linkgit:git-rev-list[1].
+`--include-edges`::
+`--no-include-edges`::
+	Include blobs from boundary commits in the backfill.  Useful in
+	preparation for commands like `git log -p A..B` or `git replay
+	--onto TARGET A..B`, where A..B normally excludes A but you need
+	the blobs from A as well.  `--include-edges` is the default.
+
+`<revision-range>`::
+	Backfill only blobs reachable from commits in the specified
+	revision range.  When no _<revision-range>_ is specified, it
+	defaults to `HEAD` (i.e. the whole history leading to the
+	current commit).  For a complete list of ways to spell
+	_<revision-range>_, see the "Specifying Ranges" section of
+	linkgit:gitrevisions[7].
++
+You may also use commit-limiting options understood by
+linkgit:git-rev-list[1] such as `--first-parent`, `--since`, or pathspecs.
++
+Most `--filter=<spec>` options don't work with the purpose of
+`git backfill`, but the `sparse:<oid>` filter is integrated to provide a
+focused set of paths to download, distinct from the `--sparse` option.
 
 SEE ALSO
 --------
diff --git a/Documentation/git-bisect.adoc b/Documentation/git-bisect.adoc
index b0078dd..d2115b2 100644
--- a/Documentation/git-bisect.adoc
+++ b/Documentation/git-bisect.adoc
@@ -8,20 +8,20 @@
 
 SYNOPSIS
 --------
-[verse]
-'git bisect' start [--term-(bad|new)=<term-new> --term-(good|old)=<term-old>]
-		   [--no-checkout] [--first-parent] [<bad> [<good>...]] [--] [<pathspec>...]
-'git bisect' (bad|new|<term-new>) [<rev>]
-'git bisect' (good|old|<term-old>) [<rev>...]
-'git bisect' terms [--term-(good|old) | --term-(bad|new)]
-'git bisect' skip [(<rev>|<range>)...]
-'git bisect' next
-'git bisect' reset [<commit>]
-'git bisect' (visualize|view)
-'git bisect' replay <logfile>
-'git bisect' log
-'git bisect' run <cmd> [<arg>...]
-'git bisect' help
+[synopsis]
+git bisect start [--term-(bad|new)=<term-new> --term-(good|old)=<term-old>]
+		 [--no-checkout] [--first-parent] [<bad> [<good>...]] [--] [<pathspec>...]
+git bisect (bad|new|<term-new>) [<rev>]
+git bisect (good|old|<term-old>) [<rev>...]
+git bisect terms [--term-(good|old) | --term-(bad|new)]
+git bisect skip [(<rev>|<range>)...]
+git bisect next
+git bisect reset [<commit>]
+git bisect (visualize|view)
+git bisect replay <logfile>
+git bisect log
+git bisect run <cmd> [<arg>...]
+git bisect help
 
 DESCRIPTION
 -----------
@@ -94,11 +94,10 @@
 ~~~~~~~~~~~~
 
 After a bisect session, to clean up the bisection state and return to
-the original HEAD, issue the following command:
+the original `HEAD`, issue the following command:
 
-------------------------------------------------
-$ git bisect reset
-------------------------------------------------
+[synopsis]
+git bisect reset
 
 By default, this will return your tree to the commit that was checked
 out before `git bisect start`.  (A new `git bisect start` will also do
@@ -107,9 +106,9 @@
 With an optional argument, you can return to a different commit
 instead:
 
-------------------------------------------------
-$ git bisect reset <commit>
-------------------------------------------------
+[synopsis]
+git bisect reset <commit>
+
 
 For example, `git bisect reset bisect/bad` will check out the first
 bad revision, while `git bisect reset HEAD` will leave you on the
@@ -143,23 +142,20 @@
 bisect start` without commits as argument and then run the following
 commands to add the commits:
 
-------------------------------------------------
+[synopsis]
 git bisect old [<rev>]
-------------------------------------------------
 
 to indicate that a commit was before the sought change, or
 
-------------------------------------------------
+[synopsis]
 git bisect new [<rev>...]
-------------------------------------------------
 
 to indicate that it was after.
 
 To get a reminder of the currently used terms, use
 
-------------------------------------------------
+[synopsis]
 git bisect terms
-------------------------------------------------
 
 You can get just the old term with `git bisect terms --term-old`
 or `git bisect terms --term-good`; `git bisect terms --term-new`
@@ -171,21 +167,20 @@
 subcommands like `reset`, `start`, ...) by starting the
 bisection using
 
-------------------------------------------------
+[synopsis]
 git bisect start --term-old <term-old> --term-new <term-new>
-------------------------------------------------
 
 For example, if you are looking for a commit that introduced a
 performance regression, you might use
 
 ------------------------------------------------
-git bisect start --term-old fast --term-new slow
+$ git bisect start --term-old fast --term-new slow
 ------------------------------------------------
 
 Or if you are looking for the commit that fixed a bug, you might use
 
 ------------------------------------------------
-git bisect start --term-new fixed --term-old broken
+$ git bisect start --term-new fixed --term-old broken
 ------------------------------------------------
 
 Then, use `git bisect <term-old>` and `git bisect <term-new>` instead
@@ -194,7 +189,7 @@
 Bisect visualize/view
 ~~~~~~~~~~~~~~~~~~~~~
 
-To see the currently remaining suspects in 'gitk', issue the following
+To see the currently remaining suspects in `gitk`, issue the following
 command during the bisection process (the subcommand `view` can be used
 as an alternative to `visualize`):
 
@@ -203,12 +198,13 @@
 ------------
 
 Git detects a graphical environment through various environment variables:
-`DISPLAY`, which is set in X Window System environments on Unix systems.
-`SESSIONNAME`, which is set under Cygwin in interactive desktop sessions.
-`MSYSTEM`, which is set under Msys2 and Git for Windows.
-`SECURITYSESSIONID`, which may be set on macOS in interactive desktop sessions.
 
-If none of these environment variables is set, 'git log' is used instead.
+`DISPLAY`:: which is set in X Window System environments on Unix systems.
+`SESSIONNAME`:: which is set under Cygwin in interactive desktop sessions.
+`MSYSTEM`:: which is set under Msys2 and Git for Windows.
+`SECURITYSESSIONID`:: which may be set on macOS in interactive desktop sessions.
+
+If none of these environment variables is set, `git log` is used instead.
 You can also give command-line options such as `-p` and `--stat`.
 
 ------------
@@ -332,18 +328,17 @@
 If you have a script that can tell if the current source code is good
 or bad, you can bisect by issuing the command:
 
-------------
-$ git bisect run my_script arguments
-------------
+[synopsis]
+git bisect run <cmd> [<arg>...]
 
-Note that the script (`my_script` in the above example) should exit
+Note that _<cmd>_ run with _<arg>_  should exit
 with code 0 if the current source code is good/old, and exit with a
 code between 1 and 127 (inclusive), except 125, if the current source
 code is bad/new.
 
 Any other exit code will abort the bisect process. It should be noted
-that a program that terminates via `exit(-1)` leaves $? = 255, (see the
-exit(3) manual page), as the value is chopped with `& 0377`.
+that a program that terminates via `exit(-1)` leaves `$?` = 255, (see the
+`exit`(3) manual page), as the value is chopped with `& 0377`.
 
 The special exit code 125 should be used when the current source code
 cannot be tested. If the script exits with this code, the current
@@ -355,12 +350,12 @@
 `bisect run` is concerned).
 
 You may often find that during a bisect session you want to have
-temporary modifications (e.g. s/#define DEBUG 0/#define DEBUG 1/ in a
+temporary modifications (e.g. `s/#define DEBUG 0/#define DEBUG 1/` in a
 header file, or "revision that does not have this commit needs this
 patch applied to work around another problem this bisection is not
 interested in") applied to the revision being tested.
 
-To cope with such a situation, after the inner 'git bisect' finds the
+To cope with such a situation, after the inner `git bisect` finds the
 next revision to test, the script can apply the patch
 before compiling, run the real test, and afterwards decide if the
 revision (possibly with the needed patch) passed the test and then
@@ -370,20 +365,18 @@
 
 OPTIONS
 -------
---no-checkout::
-+
-Do not checkout the new working tree at each iteration of the bisection
-process. Instead just update the reference named `BISECT_HEAD` to make
-it point to the commit that should be tested.
+`--no-checkout`::
+	Do not checkout the new working tree at each iteration of the bisection
+	process. Instead just update the reference named `BISECT_HEAD` to make
+	it point to the commit that should be tested.
 +
 This option may be useful when the test you would perform in each step
 does not require a checked out tree.
 +
 If the repository is bare, `--no-checkout` is assumed.
 
---first-parent::
-+
-Follow only the first parent commit upon seeing a merge commit.
+`--first-parent`::
+	Follow only the first parent commit upon seeing a merge commit.
 +
 In detecting regressions introduced through the merging of a branch, the merge
 commit will be identified as introduction of the bug and its ancestors will be
@@ -395,7 +388,7 @@
 EXAMPLES
 --------
 
-* Automatically bisect a broken build between v1.2 and HEAD:
+* Automatically bisect a broken build between v1.2 and `HEAD`:
 +
 ------------
 $ git bisect start HEAD v1.2 --      # HEAD is bad, v1.2 is good
@@ -403,7 +396,7 @@
 $ git bisect reset                   # quit the bisect session
 ------------
 
-* Automatically bisect a test failure between origin and HEAD:
+* Automatically bisect a test failure between origin and `HEAD`:
 +
 ------------
 $ git bisect start HEAD origin --    # HEAD is bad, origin is good
@@ -430,7 +423,7 @@
 +
 It is safer if both `test.sh` and `check_test_case.sh` are
 outside the repository to prevent interactions between the bisect,
-make and test processes and the scripts.
+`make` and test processes and the scripts.
 
 * Automatically bisect with temporary modifications (hot-fix):
 +
@@ -491,9 +484,9 @@
 $ git bisect reset                   # quit the bisect session
 ------------
 +
-In this case, when 'git bisect run' finishes, bisect/bad will refer to a commit that
+In this case, when `git bisect run` finishes, `bisect/bad` will refer to a commit that
 has at least one parent whose reachable graph is fully traversable in the sense
-required by 'git pack objects'.
+required by `git pack-objects`.
 
 * Look for a fix instead of a regression in the code
 +
diff --git a/Documentation/git-cat-file.adoc b/Documentation/git-cat-file.adoc
index c139f55..86b9181 100644
--- a/Documentation/git-cat-file.adoc
+++ b/Documentation/git-cat-file.adoc
@@ -174,6 +174,11 @@
 	since the beginning or since the last flush was issued. When `--buffer`
 	is used, no output will come until a `flush` is issued. When `--buffer`
 	is not used, commands are flushed each time without issuing `flush`.
+
+`mailmap (<bool>)`::
+	Enable or disable mailmap for subsequent commands. The `<bool>`
+	argument accepts the same boolean values as linkgit:git-config[1].
+	The mailmap data is read upon the first use and only once.
 --
 +
 
diff --git a/Documentation/git-checkout.adoc b/Documentation/git-checkout.adoc
index 43ccf47..a8b3b8c 100644
--- a/Documentation/git-checkout.adoc
+++ b/Documentation/git-checkout.adoc
@@ -251,20 +251,19 @@
 	are different between the current branch and the branch to
 	which you are switching, the command refuses to switch
 	branches in order to preserve your modifications in context.
-	However, with this option, a three-way merge between the current
-	branch, your working tree contents, and the new branch
-	is done, and you will be on the new branch.
-+
-When a merge conflict happens, the index entries for conflicting
-paths are left unmerged, and you need to resolve the conflicts
-and mark the resolved paths with `git add` (or `git rm` if the merge
-should result in deletion of the path).
+	With this option, the conflicting local changes are
+	automatically stashed before the switch and reapplied
+	afterwards.  If the local changes do not overlap with the
+	differences between branches, the switch proceeds without
+	stashing.  If reapplying the stash results in conflicts, the
+	entry is saved to the stash list.  Resolve the conflicts
+	and run `git stash drop` when done, or clear the working
+	tree (e.g. with `git reset --hard`) before running `git stash
+	pop` later to re-apply your changes.
 +
 When checking out paths from the index, this option lets you recreate
 the conflicted merge in the specified paths.  This option cannot be
 used when checking out paths from a tree-ish.
-+
-When switching branches with `--merge`, staged changes may be lost.
 
 `--conflict=<style>`::
 	The same as `--merge` option above, but changes the way the
@@ -578,38 +577,36 @@
 error: You have local changes to 'frotz'; not switching branches.
 ------------
 
-You can give the `-m` flag to the command, which would try a
-three-way merge:
+You can give the `-m` flag to the command, which will carry your local
+changes to the new branch:
 
 ------------
 $ git checkout -m mytopic
-Auto-merging frotz
+Applied autostash.
+Switched to branch 'mytopic'
+The following paths have local changes:
+M	frotz
 ------------
 
-After this three-way merge, the local modifications are _not_
+After the switch, the local modifications are reapplied and are _not_
 registered in your index file, so `git diff` would show you what
 changes you made since the tip of the new branch.
 
 === 3. Merge conflict
 
-When a merge conflict happens during switching branches with
-the `-m` option, you would see something like this:
+When the `--merge` (`-m`) option is given and the local changes
+overlap with the changes in the branch we're switching to, the
+changes are stashed and reapplied after the switch.  If this
+process results in conflicts, the stash entry is saved and a
+message is printed:
 
 ------------
 $ git checkout -m mytopic
-Auto-merging frotz
-ERROR: Merge conflict in frotz
-fatal: merge program failed
-------------
-
-At this point, `git diff` shows the changes cleanly merged as in
-the previous example, as well as the changes in the conflicted
-files.  Edit and resolve the conflict and mark it resolved with
-`git add` as usual:
-
-------------
-$ edit frotz
-$ git add frotz
+Your local changes are stashed, however applying them
+resulted in conflicts.  You can either resolve the conflicts
+and then discard the stash with "git stash drop", or, if you
+do not want to resolve them now, run "git reset --hard" and
+apply the local changes later by running "git stash pop".
 ------------
 
 CONFIGURATION
diff --git a/Documentation/git-commit-graph.adoc b/Documentation/git-commit-graph.adoc
index 6d19026..f2a37e9 100644
--- a/Documentation/git-commit-graph.adoc
+++ b/Documentation/git-commit-graph.adoc
@@ -146,6 +146,12 @@
 $ git rev-parse HEAD | git commit-graph write --stdin-commits --append
 ------------------------------------------------
 
+CAVEATS
+-------
+
+The existence of replace objects or commit grafts turns off reading or
+writing to the commit-graph. See linkgit:git-replace[1].
+
 CONFIGURATION
 -------------
 
diff --git a/Documentation/git-describe.adoc b/Documentation/git-describe.adoc
index 08ff715..b2cb1e4 100644
--- a/Documentation/git-describe.adoc
+++ b/Documentation/git-describe.adoc
@@ -7,10 +7,10 @@
 
 SYNOPSIS
 --------
-[verse]
-'git describe' [--all] [--tags] [--contains] [--abbrev=<n>] [<commit-ish>...]
-'git describe' [--all] [--tags] [--contains] [--abbrev=<n>] --dirty[=<mark>]
-'git describe' <blob>
+[synopsis]
+git describe [--all] [--tags] [--contains] [--abbrev=<n>] [<commit-ish>...]
+git describe [--all] [--tags] [--contains] [--abbrev=<n>] --dirty[=<mark>]
+git describe <blob>
 
 DESCRIPTION
 -----------
@@ -22,70 +22,70 @@
 is a "human-readable" object name which can also be used to
 identify the commit to other git commands.
 
-By default (without --all or --tags) `git describe` only shows
+By default (without `--all` or `--tags`) `git describe` only shows
 annotated tags.  For more information about creating annotated tags
-see the -a and -s options to linkgit:git-tag[1].
+see the `-a` and `-s` options to linkgit:git-tag[1].
 
 If the given object refers to a blob, it will be described
 as `<commit-ish>:<path>`, such that the blob can be found
-at `<path>` in the `<commit-ish>`, which itself describes the
+at _<path>_ in the _<commit-ish>_, which itself describes the
 first commit in which this blob occurs in a reverse revision walk
-from HEAD.
+from `HEAD`.
 
 OPTIONS
 -------
-<commit-ish>...::
-	Commit-ish object names to describe.  Defaults to HEAD if omitted.
+`<commit-ish>...`::
+	Commit-ish object names to describe. Defaults to `HEAD` if omitted.
 
---dirty[=<mark>]::
---broken[=<mark>]::
+`--dirty[=<mark>]`::
+`--broken[=<mark>]`::
 	Describe the state of the working tree.  When the working
-	tree matches HEAD, the output is the same as "git describe
-	HEAD".  If the working tree has local modification "-dirty"
+	tree matches `HEAD`, the output is the same as `git describe HEAD`.
+	If the working tree has local modification, `-dirty`
 	is appended to it.  If a repository is corrupt and Git
 	cannot determine if there is local modification, Git will
-	error out, unless `--broken' is given, which appends
-	the suffix "-broken" instead.
+	error out, unless `--broken` is given, which appends
+	the suffix `-broken` instead.
 
---all::
+`--all`::
 	Instead of using only the annotated tags, use any ref
 	found in `refs/` namespace.  This option enables matching
 	any known branch, remote-tracking branch, or lightweight tag.
 
---tags::
+`--tags`::
 	Instead of using only the annotated tags, use any tag
 	found in `refs/tags` namespace.  This option enables matching
 	a lightweight (non-annotated) tag.
 
---contains::
+`--contains`::
 	Instead of finding the tag that predates the commit, find
 	the tag that comes after the commit, and thus contains it.
-	Automatically implies --tags.
+	Automatically implies `--tags`.
 
---abbrev=<n>::
+`--abbrev=<n>`::
 	Instead of using the default number of hexadecimal digits (which
 	will vary according to the number of objects in the repository with
-	a default of 7) of the abbreviated object name, use <n> digits, or
-	as many digits as needed to form a unique object name. An <n> of 0
+	a default of 7) of the abbreviated object name, use _<n>_ digits, or
+	as many digits as needed to form a unique object name. An _<n>_ of 0
 	will suppress long format, only showing the closest tag.
 
---candidates=<n>::
+`--candidates=<n>`::
 	Instead of considering only the 10 most recent tags as
 	candidates to describe the input commit-ish consider
-	up to <n> candidates.  Increasing <n> above 10 will take
+	up to _<n>_ candidates.  Increasing _<n>_ above 10 will take
 	slightly longer but may produce a more accurate result.
-	An <n> of 0 will cause only exact matches to be output.
+	An _<n>_ of 0 will cause only exact matches to be output.
 
---exact-match::
+`--exact-match`::
 	Only output exact matches (a tag directly references the
-	supplied commit).  This is a synonym for --candidates=0.
+	supplied commit).  This is a synonym for `--candidates=0`.
 
---debug::
+`--debug`::
 	Verbosely display information about the searching strategy
 	being employed to standard error.  The tag name will still
 	be printed to standard out.
 
---long::
+`--long`::
 	Always output the long format (the tag, the number of commits
 	and the abbreviated commit name) even when it matches a tag.
 	This is useful when you want to see parts of the commit object name
@@ -94,8 +94,8 @@
 	describe such a commit as v1.2-0-gdeadbee (0th commit since tag v1.2
 	that points at object deadbee....).
 
---match <pattern>::
-	Only consider tags matching the given `glob(7)` pattern,
+`--match <pattern>`::
+	Only consider tags matching the given `glob`(7) pattern,
 	excluding the "refs/tags/" prefix. If used with `--all`, it also
 	considers local branches and remote-tracking references matching the
 	pattern, excluding respectively "refs/heads/" and "refs/remotes/"
@@ -104,22 +104,22 @@
 	matching any of the patterns will be considered.  Use `--no-match` to
 	clear and reset the list of patterns.
 
---exclude <pattern>::
-	Do not consider tags matching the given `glob(7)` pattern, excluding
+`--exclude <pattern>`::
+	Do not consider tags matching the given `glob`(7) pattern, excluding
 	the "refs/tags/" prefix. If used with `--all`, it also does not consider
 	local branches and remote-tracking references matching the pattern,
-	excluding respectively "refs/heads/" and "refs/remotes/" prefix;
+	excluding respectively "`refs/heads/`" and "`refs/remotes/`" prefix;
 	references of other types are never considered. If given multiple times,
 	a list of patterns will be accumulated and tags matching any of the
-	patterns will be excluded. When combined with --match a tag will be
-	considered when it matches at least one --match pattern and does not
-	match any of the --exclude patterns. Use `--no-exclude` to clear and
+	patterns will be excluded. When combined with `--match` a tag will be
+	considered when it matches at least one `--match` pattern and does not
+	match any of the `--exclude` patterns. Use `--no-exclude` to clear and
 	reset the list of patterns.
 
---always::
+`--always`::
 	Show uniquely abbreviated commit object as fallback.
 
---first-parent::
+`--first-parent`::
 	Follow only the first parent commit upon seeing a merge commit.
 	This is useful when you wish to not match tags on branches merged
 	in the history of the target commit.
@@ -139,8 +139,8 @@
 at the end.
 
 The number of additional commits is the number
-of commits which would be displayed by "git log v1.0.4..parent".
-The hash suffix is "-g" + an unambiguous abbreviation for the tip commit
+of commits which would be displayed by `git log v1.0.4..parent`.
+The hash suffix is "`-g`" + an unambiguous abbreviation for the tip commit
 of parent (which was `2414721b194453f058079d897d13c4e377f92dc6`). The
 length of the abbreviation scales as the repository grows, using the
 approximate number of objects in the repository and a bit of math
@@ -149,12 +149,12 @@
 a software depending on the SCM the software is managed with. This is useful
 in an environment where people may use different SCMs.
 
-Doing a 'git describe' on a tag-name will just show the tag name:
+Doing a `git describe` on a tag-name will just show the tag name:
 
 	[torvalds@g5 git]$ git describe v1.0.4
 	v1.0.4
 
-With --all, the command can use branch heads as references, so
+With `--all`, the command can use branch heads as references, so
 the output shows the reference path as well:
 
 	[torvalds@g5 git]$ git describe --all --abbrev=4 v1.0.5^2
@@ -163,7 +163,7 @@
 	[torvalds@g5 git]$ git describe --all --abbrev=4 HEAD^
 	heads/lt/describe-7-g975b
 
-With --abbrev set to 0, the command can be used to find the
+With `--abbrev` set to 0, the command can be used to find the
 closest tagname without any suffix:
 
 	[torvalds@g5 git]$ git describe --abbrev=0 v1.0.5^2
@@ -179,13 +179,13 @@
 SEARCH STRATEGY
 ---------------
 
-For each commit-ish supplied, 'git describe' will first look for
+For each commit-ish supplied, `git describe` will first look for
 a tag which tags exactly that commit.  Annotated tags will always
 be preferred over lightweight tags, and tags with newer dates will
 always be preferred over tags with older dates.  If an exact match
 is found, its name will be output and searching will stop.
 
-If an exact match was not found, 'git describe' will walk back
+If an exact match was not found, `git describe` will walk back
 through the commit history to locate an ancestor commit which
 has been tagged.  The ancestor's tag will be output along with an
 abbreviation of the input commit-ish's SHA-1. If `--first-parent` was
@@ -203,7 +203,7 @@
 
 Tree objects as well as tag objects not pointing at commits, cannot be described.
 When describing blobs, the lightweight tags pointing at blobs are ignored,
-but the blob is still described as <commit-ish>:<path> despite the lightweight
+but the blob is still described as `<commit-ish>:<path>` despite the lightweight
 tag being favorable.
 
 GIT
diff --git a/Documentation/git-difftool.adoc b/Documentation/git-difftool.adoc
index 064bc68..dd7cacf 100644
--- a/Documentation/git-difftool.adoc
+++ b/Documentation/git-difftool.adoc
@@ -7,64 +7,64 @@
 
 SYNOPSIS
 --------
-[verse]
-'git difftool' [<options>] [<commit> [<commit>]] [--] [<path>...]
+[synopsis]
+git difftool [<options>] [<commit> [<commit>]] [--] [<path>...]
 
 DESCRIPTION
 -----------
-'git difftool' is a Git command that allows you to compare and edit files
-between revisions using common diff tools.  'git difftool' is a frontend
-to 'git diff' and accepts the same options and arguments. See
+`git difftool` is a Git command that allows you to compare and edit files
+between revisions using common diff tools. `git difftool` is a frontend
+to `git diff` and accepts the same options and arguments. See
 linkgit:git-diff[1].
 
 OPTIONS
 -------
--d::
---dir-diff::
+`-d`::
+`--dir-diff`::
 	Copy the modified files to a temporary location and perform
 	a directory diff on them. This mode never prompts before
 	launching the diff tool.
 
--y::
---no-prompt::
+`-y`::
+`--no-prompt`::
 	Do not prompt before launching a diff tool.
 
---prompt::
+`--prompt`::
 	Prompt before each invocation of the diff tool.
 	This is the default behaviour; the option is provided to
 	override any configuration settings.
 
---rotate-to=<file>::
-	Start showing the diff for the given path,
+`--rotate-to=<file>`::
+	Start showing the diff for _<file>_,
 	the paths before it will move to the end and output.
 
---skip-to=<file>::
-	Start showing the diff for the given path, skipping all
+`--skip-to=<file>`::
+	Start showing the diff for _<file>_, skipping all
 	the paths before it.
 
--t <tool>::
---tool=<tool>::
-	Use the diff tool specified by <tool>.  Valid values include
+`-t <tool>`::
+`--tool=<tool>`::
+	Use the diff tool specified by _<tool>_. Valid values include
 	emerge, kompare, meld, and vimdiff. Run `git difftool --tool-help`
-	for the list of valid <tool> settings.
+	for the list of valid _<tool>_ settings.
 +
-If a diff tool is not specified, 'git difftool'
+If a diff tool is not specified, `git difftool`
 will use the configuration variable `diff.tool`.  If the
-configuration variable `diff.tool` is not set, 'git difftool'
+configuration variable `diff.tool` is not set, `git difftool`
 will pick a suitable default.
 +
 You can explicitly provide a full path to the tool by setting the
 configuration variable `difftool.<tool>.path`. For example, you
 can configure the absolute path to kdiff3 by setting
-`difftool.kdiff3.path`. Otherwise, 'git difftool' assumes the
+`difftool.kdiff3.path`. Otherwise, `git difftool` assumes the
 tool is available in PATH.
 +
 Instead of running one of the known diff tools,
-'git difftool' can be customized to run an alternative program
+`git difftool` can be customized to run an alternative program
 by specifying the command line to invoke in a configuration
 variable `difftool.<tool>.cmd`.
 +
-When 'git difftool' is invoked with this tool (either through the
+When `git difftool` is invoked with this tool (either through the
 `-t` or `--tool` option or the `diff.tool` configuration variable)
 the configured command line will be invoked with the following
 variables available: `$LOCAL` is set to the name of the temporary
@@ -74,30 +74,30 @@
 being compared. `$BASE` is provided for compatibility
 with custom merge tool commands and has the same value as `$MERGED`.
 
---tool-help::
+`--tool-help`::
 	Print a list of diff tools that may be used with `--tool`.
 
---symlinks::
---no-symlinks::
-	'git difftool''s default behavior is to create symlinks to the
+`--symlinks`::
+`--no-symlinks`::
+	`git difftool`'s default behavior is to create symlinks to the
 	working tree when run in `--dir-diff` mode and the right-hand
 	side of the comparison yields the same content as the file in
 	the working tree.
 +
-Specifying `--no-symlinks` instructs 'git difftool' to create copies
+Specifying `--no-symlinks` instructs `git difftool` to create copies
 instead.  `--no-symlinks` is the default on Windows.
 
--x <command>::
---extcmd=<command>::
+`-x <command>`::
+`--extcmd=<command>`::
 	Specify a custom command for viewing diffs.
-	'git-difftool' ignores the configured defaults and runs
+	`git-difftool` ignores the configured defaults and runs
 	`<command> $LOCAL $REMOTE` when this option is specified.
 	Additionally, `$BASE` is set in the environment.
 
--g::
---gui::
---no-gui::
-	When 'git-difftool' is invoked with the `-g` or `--gui` option
+`-g`::
+`--gui`::
+`--no-gui`::
+	When `git-difftool` is invoked with the `-g` or `--gui` option
 	the default diff tool will be read from the configured
 	`diff.guitool` variable instead of `diff.tool`. This may be
 	selected automatically using the configuration variable
@@ -106,20 +106,20 @@
 	fallback in the order of `merge.guitool`, `diff.tool`,
 	`merge.tool` until a tool is found.
 
---trust-exit-code::
---no-trust-exit-code::
+`--trust-exit-code`::
+`--no-trust-exit-code`::
 	Errors reported by the diff tool are ignored by default.
-	Use `--trust-exit-code` to make 'git-difftool' exit when an
+	Use `--trust-exit-code` to make `git-difftool` exit when an
 	invoked diff tool returns a non-zero exit code.
 +
-'git-difftool' will forward the exit code of the invoked tool when
+`git-difftool` will forward the exit code of the invoked tool when
 `--trust-exit-code` is used.
 
 See linkgit:git-diff[1] for the full list of supported options.
 
 CONFIGURATION
 -------------
-'git difftool' falls back to 'git mergetool' config variables when the
+`git difftool` falls back to `git mergetool` config variables when the
 difftool equivalents have not been defined.
 
 include::includes/cmd-config-section-rest.adoc[]
diff --git a/Documentation/git-fast-import.adoc b/Documentation/git-fast-import.adoc
index b3f42d4..d68bc52 100644
--- a/Documentation/git-fast-import.adoc
+++ b/Documentation/git-fast-import.adoc
@@ -66,11 +66,10 @@
 remote-helpers that use the `import` capability, as they are
 already trusted to run their own code.
 
-`--signed-tags=(verbatim|warn-verbatim|warn-strip|strip|abort)`::
+`--signed-tags=<mode>`::
 	Specify how to handle signed tags. Behaves in the same way as
-	the `--signed-commits=<mode>` below, except that the
-	`strip-if-invalid` mode is not yet supported. Like for signed
-	commits, the default mode is `verbatim`.
+	the `--signed-commits=<mode>` below. Like for signed commits,
+	the default mode is `verbatim`.
 
 `--signed-commits=<mode>`::
 	Specify how to handle signed commits. The following <mode>s
@@ -90,6 +89,8 @@
   commit signatures and replaces invalid signatures with newly created ones.
   Valid signatures are left unchanged. If `<keyid>` is provided, that key is
   used for signing; otherwise the configured default signing key is used.
+* `abort-if-invalid` will make this program die when encountering a signed
+  commit that is unable to be verified.
 
 Options for Frontends
 ~~~~~~~~~~~~~~~~~~~~~
diff --git a/Documentation/git-format-rev.adoc b/Documentation/git-format-rev.adoc
new file mode 100644
index 0000000..c40d52e
--- /dev/null
+++ b/Documentation/git-format-rev.adoc
@@ -0,0 +1,215 @@
+git-format-rev(1)
+=================
+
+NAME
+----
+git-format-rev - EXPERIMENTAL: Pretty format revisions on demand
+
+
+SYNOPSIS
+--------
+[synopsis]
+(EXPERIMENTAL!) git format-rev --stdin-mode=<mode> --format=<pretty> [--[no-]notes=<ref>] [-z] [--[no-]null-output] [--[no-]null-input]
+
+DESCRIPTION
+-----------
+
+Pretty format revisions from standard input.
+
+THIS COMMAND IS EXPERIMENTAL. THE BEHAVIOR MAY CHANGE.
+
+OPTIONS
+-------
+
+`--stdin-mode=<mode>`::
+	How to interpret standard input data:
++
+--
+`revs`;; Each line or record (see the <<io,INPUT AND OUTPUT FORMATS>>
+	section) is interpreted as a commit. Any kind of revision
+	expression can be used (see linkgit:gitrevisions[7]). Annotated
+	tags are peeled (see linkgit:gitglossary[7]).
++
+The argument `rev` is also accepted.
+
+`text`;; Formats all commit object names found in freeform text. These
+	must the full object names, i.e. abbreviated hexidecimal object
+	names will not be interpreted.
++
+Anything that is parsed as an object name but that is not found to be a
+commit object name is left alone (echoed).
+--
+
+`--format=<pretty>`::
+	Pretty format string.
+
+`--notes=<ref>`::
+`--no-notes`::
+	Custom notes ref. Notes are displayed when using the `%N`
+	atom. See linkgit:git-notes[1].
+
+`-z`::
+`--null`::
+	Use _NUL_ character to terminate both input and output instead
+	of newline. This option cannot be negated.
++
+This is useful if both the input and output could contain newlines or if
+the input to this command also uses _NUL_ character termination; see the
+<<io,INPUT AND OUTPUT FORMATS>> section below.
++
+The mode `--stdin-mode=text` can have use for this option when it needs
+to process input like for example `git last-modified -z`; see the
+<<examples,EXAMPLES>> section below.
+
+`--null-output`::
+`--no-null-output`::
+	Use _NUL_ character to terminate output instead of newline. The
+	default is `--no-null-output`.
++
+This is useful if the output could contain newlines, for example if the
+`%n` (newline) atom is used.
+
+`--null-input`::
+`--no-null-input`::
+	Use _NUL_ character to terminate input instead of newline. The
+	default is `--no-null-input`.
++
+This is useful if the input revision expressions could contain newlines.
+
+[[io]]
+INPUT AND OUTPUT FORMAT
+-----------------------
+
+The command uses newlines for both input and output termination by
+default. See the `-z`, `--null-output`, and `--null-input` options for
+using _NUL_ character as the terminator.
+
+The mode `--stdin-mode=revs` outputs one formatted commit followed by
+the terminator. This could either be called a _line_ or a _record_ in
+case "line" is too suggestive of newline termination.
+
+Note that this means that the terminator character (newline or _NUL_)
+acts as a _terminator_, not a _separator_. In other words, the final
+line or record is also terminated by the terminator character.
+
+The mode `--stdin-mode=text` replaces each object name with the
+formatted commit, i.e. the format `%s` would transform some commit
+object name to `<subject>` without any termination. Like this:
+
+----
+Did we not fix this in "<subject>"?
+----
+
+It is safe to interactively read and write from this command since each
+record is immediately flushed.
+
+[[examples]]
+EXAMPLES
+--------
+
+The command linkgit:git-last-modified[1] shows the commit that each file
+was last modified in.
+
+----
+$ git last-modified -- README.md Makefile
+7798034171030be0909c56377a4e0e10e6d2df93	Makefile
+c50fbb2dd225e7e82abba4380423ae105089f4d7	README.md
+----
+
+We can pipe the result to this command in order to replace the object
+name with the commit author.
+
+----
+$ git last-modified -- README.md Makefile |
+    git format-rev --stdin-mode=text --format=%an
+Junio C Hamano	Makefile
+Todd Zullinger	README.md
+----
+
+Another example is _formatting commits in commit messages_. Given this commit message:
+
+----
+Fix off-by-one error
+
+Fix off-by-one error introduced in
+e83c5163316f89bfbde7d9ab23ca2e25604af290.
+
+We thought we fixed this in 5569bf9bbedd63a00780fc5c110e0cfab3aa97b9 but
+that only covered 1/3 of the faulty cases.
+----
+
+We can format the commits and use par(1) to reflow the text, say in a
+`commit-msg` hook:
+
+----
+$ git config set hook.reference-commits.event commit-msg
+$ git config set hook.reference-commits.command reference-commits
+$ cat $(which reference-commits)
+#/bin/sh
+
+msg="$1"
+rewritten=$(mktemp)
+git format-rev --stdin-mode=text --format=reference <"$msg" |
+    par >"$rewritten"
+mv "$rewritten" "$msg"
+----
+
+Which will produce something like this:
+
+----
+Fix off-by-one error
+
+Fix off-by-one error introduced in e83c5163316 (Implement better memory
+allocator, 2005-04-07).
+
+We thought we fixed this in 5569bf9bbed (Fix memory allocator,
+2005-06-22) but that only covered 1/3 of the faulty cases.
+----
+
+DISCUSSION
+----------
+
+This command lets you format any number of revisions in any order
+through one command invocation. Consider the
+linkgit:git-last-modified[1] case from the <<examples,EXAMPLES>> section
+above:
+
+1. There might be hundreds of files
+2. Commits can be repeated, i.e. two or more files were last modified in
+   the same commit
+
+Two widely-used commands which pretty formats commits are
+linkgit:git-log[1] and linkgit:git-show[1]. It turns out that they are
+not a good fit for the above use case.
+
+- The output of linkgit:git-last-modified[1] would have to be processed
+  in stages since you need to transform the first column separately and
+  then link the author to the filename. But this is surmountable.
+- You can feed each commit to `git show` or `git log --no-walk -1`. But
+  that means that you need to create a process for each line.
+- Let’s say that you want to use one process, not one per line. So you
+  want to feed all the commits to the command. Now you face the problem
+  that you have to feed all the commits to the commands before you get
+  any output (this is also the case for the `--stdin` modes). In other
+  words, you cannot loop through each line, get the author for the
+  commit, and output the author and the filename. You need to feed all
+  the commits, get back all the output, and match the output with the
+  filename.
+- But the next problem is that commands will deduplicate the input and
+  only output one commit one single time only. Thus you cannot make the
+  output order match the input order, since a commit could have been
+  repeated in the original input.
+
+In short, it is straightforward to use these two commands if you use one
+process per line. It is much more work if you just want to use one
+process, but still doable. In contrast, this problem is solved with just
+another shell pipeline with this command.
+
+SEE ALSO
+--------
+linkgit:git-name-rev[1],
+linkgit:git-log[1].
+
+GIT
+---
+Part of the linkgit:git[1] suite
diff --git a/Documentation/git-fsmonitor--daemon.adoc b/Documentation/git-fsmonitor--daemon.adoc
index 8fe5241..12fa866 100644
--- a/Documentation/git-fsmonitor--daemon.adoc
+++ b/Documentation/git-fsmonitor--daemon.adoc
@@ -76,9 +76,9 @@
 correctly with all network-mounted repositories, so such use is considered
 experimental.
 
-On Mac OS, the inter-process communication (IPC) between various Git
+On Mac OS and Linux, the inter-process communication (IPC) between various Git
 commands and the fsmonitor daemon is done via a Unix domain socket (UDS) -- a
-special type of file -- which is supported by native Mac OS filesystems,
+special type of file -- which is supported by native Mac OS and Linux filesystems,
 but not on network-mounted filesystems, NTFS, or FAT32.  Other filesystems
 may or may not have the needed support; the fsmonitor daemon is not guaranteed
 to work with these filesystems and such use is considered experimental.
@@ -87,13 +87,33 @@
 `.git` directory is on a network-mounted filesystem, it will instead be
 created at `$HOME/.git-fsmonitor-*` unless `$HOME` itself is on a
 network-mounted filesystem, in which case you must set the configuration
-variable `fsmonitor.socketDir` to the path of a directory on a Mac OS native
+variable `fsmonitor.socketDir` to the path of a directory on a native
 filesystem in which to create the socket file.
 
 If none of the above directories (`.git`, `$HOME`, or `fsmonitor.socketDir`)
-is on a native Mac OS file filesystem the fsmonitor daemon will report an
+is on a native filesystem the fsmonitor daemon will report an
 error that will cause the daemon and the currently running command to exit.
 
+LINUX CAVEATS
+~~~~~~~~~~~~~
+
+On Linux, the fsmonitor daemon uses inotify to monitor filesystem events.
+The inotify system has per-user limits on the number of watches that can
+be created.  The default limit is typically 8192 watches per user.
+
+For large repositories with many directories, you may need to increase
+this limit.  Check the current limit with:
+
+    cat /proc/sys/fs/inotify/max_user_watches
+
+To temporarily increase the limit:
+
+    sudo sysctl fs.inotify.max_user_watches=65536
+
+To make the change permanent, add to `/etc/sysctl.conf`:
+
+    fs.inotify.max_user_watches=65536
+
 CONFIGURATION
 -------------
 
diff --git a/Documentation/git-grep.adoc b/Documentation/git-grep.adoc
index a548585..19b3ade 100644
--- a/Documentation/git-grep.adoc
+++ b/Documentation/git-grep.adoc
@@ -8,8 +8,8 @@
 
 SYNOPSIS
 --------
-[verse]
-'git grep' [-a | --text] [-I] [--textconv] [-i | --ignore-case] [-w | --word-regexp]
+[synopsis]
+git grep [-a | --text] [-I] [--textconv] [-i | --ignore-case] [-w | --word-regexp]
 	   [-v | --invert-match] [-h|-H] [--full-name]
 	   [-E | --extended-regexp] [-G | --basic-regexp]
 	   [-P | --perl-regexp]
@@ -41,139 +41,139 @@
 
 OPTIONS
 -------
---cached::
+`--cached`::
 	Instead of searching tracked files in the working tree, search
 	blobs registered in the index file.
 
---untracked::
+`--untracked`::
 	In addition to searching in the tracked files in the working
 	tree, search also in untracked files.
 
---no-index::
+`--no-index`::
 	Search files in the current directory that is not managed by Git,
 	or by ignoring that the current directory is managed by Git.  This
-	is rather similar to running the regular `grep(1)` utility with its
+	is rather similar to running the regular `grep`(1) utility with its
 	`-r` option specified, but with some additional benefits, such as
-	using pathspec patterns to limit paths;  see the 'pathspec' entry
+	using pathspec patterns to limit paths;  see the `pathspec` entry
 	in linkgit:gitglossary[7] for more information.
 +
 This option cannot be used together with `--cached` or `--untracked`.
 See also `grep.fallbackToNoIndex` in 'CONFIGURATION' below.
 
---no-exclude-standard::
+`--no-exclude-standard`::
 	Also search in ignored files by not honoring the `.gitignore`
 	mechanism. Only useful with `--untracked`.
 
---exclude-standard::
+`--exclude-standard`::
 	Do not pay attention to ignored files specified via the `.gitignore`
 	mechanism.  Only useful when searching files in the current directory
 	with `--no-index`.
 
---recurse-submodules::
+`--recurse-submodules`::
 	Recursively search in each submodule that is active and
 	checked out in the repository.  When used in combination with the
 	_<tree>_ option the prefix of all submodule output will be the name of
 	the parent project's _<tree>_ object.  This option cannot be used together
 	with `--untracked`, and it has no effect if `--no-index` is specified.
 
--a::
---text::
+`-a`::
+`--text`::
 	Process binary files as if they were text.
 
---textconv::
+`--textconv`::
 	Honor textconv filter settings.
 
---no-textconv::
+`--no-textconv`::
 	Do not honor textconv filter settings.
 	This is the default.
 
--i::
---ignore-case::
+`-i`::
+`--ignore-case`::
 	Ignore case differences between the patterns and the
 	files.
 
--I::
+`-I`::
 	Don't match the pattern in binary files.
 
---max-depth <depth>::
-	For each <pathspec> given on command line, descend at most <depth>
+`--max-depth <depth>`::
+	For each _<pathspec>_ given on command line, descend at most _<depth>_
 	levels of directories. A value of -1 means no limit.
-	This option is ignored if <pathspec> contains active wildcards.
+	This option is ignored if _<pathspec>_ contains active wildcards.
 	In other words if "a*" matches a directory named "a*",
-	"*" is matched literally so --max-depth is still effective.
+	"*" is matched literally so `--max-depth` is still effective.
 
--r::
---recursive::
+`-r`::
+`--recursive`::
 	Same as `--max-depth=-1`; this is the default.
 
---no-recursive::
+`--no-recursive`::
 	Same as `--max-depth=0`.
 
--w::
---word-regexp::
+`-w`::
+`--word-regexp`::
 	Match the pattern only at word boundary (either begin at the
 	beginning of a line, or preceded by a non-word character; end at
 	the end of a line or followed by a non-word character).
 
--v::
---invert-match::
+`-v`::
+`--invert-match`::
 	Select non-matching lines.
 
--h::
--H::
+`-h`::
+`-H`::
 	By default, the command shows the filename for each
 	match.  `-h` option is used to suppress this output.
 	`-H` is there for completeness and does not do anything
 	except it overrides `-h` given earlier on the command
 	line.
 
---full-name::
+`--full-name`::
 	When run from a subdirectory, the command usually
 	outputs paths relative to the current directory.  This
 	option forces paths to be output relative to the project
 	top directory.
 
--E::
---extended-regexp::
--G::
---basic-regexp::
+`-E`::
+`--extended-regexp`::
+`-G`::
+`--basic-regexp`::
 	Use POSIX extended/basic regexp for patterns.  Default
 	is to use basic regexp.
 
--P::
---perl-regexp::
+`-P`::
+`--perl-regexp`::
 	Use Perl-compatible regular expressions for patterns.
 +
 Support for these types of regular expressions is an optional
 compile-time dependency. If Git wasn't compiled with support for them
 providing this option will cause it to die.
 
--F::
---fixed-strings::
+`-F`::
+`--fixed-strings`::
 	Use fixed strings for patterns (don't interpret pattern
 	as a regex).
 
--n::
---line-number::
+`-n`::
+`--line-number`::
 	Prefix the line number to matching lines.
 
---column::
+`--column`::
 	Prefix the 1-indexed byte-offset of the first match from the start of the
 	matching line.
 
--l::
---files-with-matches::
---name-only::
--L::
---files-without-match::
+`-l`::
+`--files-with-matches`::
+`--name-only`::
+`-L`::
+`--files-without-match`::
 	Instead of showing every matched line, show only the
 	names of files that contain (or do not contain) matches.
-	For better compatibility with 'git diff', `--name-only` is a
+	For better compatibility with `git diff`, `--name-only` is a
 	synonym for `--files-with-matches`.
 
--O[<pager>]::
---open-files-in-pager[=<pager>]::
-	Open the matching files in the pager (not the output of 'grep').
+`-O[<pager>]`::
+`--open-files-in-pager[=<pager>]`::
+	Open the matching files in the pager (not the output of `grep`).
 	If the pager happens to be "less" or "vi", and the user
 	specified only one pattern, the first file is positioned at
 	the first match automatically. The `pager` argument is
@@ -181,65 +181,65 @@
 	without a space. If `pager` is unspecified, the default pager
 	will be used (see `core.pager` in linkgit:git-config[1]).
 
--z::
---null::
+`-z`::
+`--null`::
 	Use \0 as the delimiter for pathnames in the output, and print
 	them verbatim. Without this option, pathnames with "unusual"
 	characters are quoted as explained for the configuration
 	variable `core.quotePath` (see linkgit:git-config[1]).
 
--o::
---only-matching::
+`-o`::
+`--only-matching`::
 	Print only the matched (non-empty) parts of a matching line, with each such
 	part on a separate output line.
 
--c::
---count::
+`-c`::
+`--count`::
 	Instead of showing every matched line, show the number of
 	lines that match.
 
---color[=<when>]::
+`--color[=<when>]`::
 	Show colored matches.
-	The value must be always (the default), never, or auto.
+	The value must be `always` (the default), `never`, or `auto`.
 
---no-color::
+`--no-color`::
 	Turn off match highlighting, even when the configuration file
 	gives the default to color output.
 	Same as `--color=never`.
 
---break::
+`--break`::
 	Print an empty line between matches from different files.
 
---heading::
+`--heading`::
 	Show the filename above the matches in that file instead of
 	at the start of each shown line.
 
--p::
---show-function::
+`-p`::
+`--show-function`::
 	Show the preceding line that contains the function name of
 	the match, unless the matching line is a function name itself.
 	The name is determined in the same way as `git diff` works out
 	patch hunk headers (see 'Defining a custom hunk-header' in
 	linkgit:gitattributes[5]).
 
--<num>::
--C <num>::
---context <num>::
-	Show <num> leading and trailing lines, and place a line
+`-<num>`::
+`-C <num>`::
+`--context <num>`::
+	Show _<num>_ leading and trailing lines, and place a line
 	containing `--` between contiguous groups of matches.
 
--A <num>::
---after-context <num>::
-	Show <num> trailing lines, and place a line containing
+`-A <num>`::
+`--after-context <num>`::
+	Show _<num>_ trailing lines, and place a line containing
 	`--` between contiguous groups of matches.
 
--B <num>::
---before-context <num>::
-	Show <num> leading lines, and place a line containing
+`-B <num>`::
+`--before-context <num>`::
+	Show _<num>_ leading lines, and place a line containing
 	`--` between contiguous groups of matches.
 
--W::
---function-context::
+`-W`::
+`--function-context`::
 	Show the surrounding text from the previous line containing a
 	function name up to the one before the next function name,
 	effectively showing the whole function in which the match was
@@ -247,22 +247,22 @@
 	`git diff` works out patch hunk headers (see 'Defining a
 	custom hunk-header' in linkgit:gitattributes[5]).
 
--m <num>::
---max-count <num>::
+`-m <num>`::
+`--max-count <num>`::
 	Limit the amount of matches per file. When using the `-v` or
 	`--invert-match` option, the search stops after the specified
 	number of non-matches. A value of -1 will return unlimited
 	results (the default). A value of 0 will exit immediately with
 	a non-zero status.
 
---threads <num>::
-	Number of `grep` worker threads to use.  See 'NOTES ON THREADS'
+`--threads <num>`::
+	Number of `grep` worker threads to use.  See `NOTES ON THREADS`
 	and `grep.threads` in 'CONFIGURATION' for more information.
 
--f <file>::
-	Read patterns from <file>, one per line.
+`-f <file>`::
+	Read patterns from _<file>_, one per line.
 +
-Passing the pattern via <file> allows for providing a search pattern
+Passing the pattern via _<file>_ allows for providing a search pattern
 containing a \0.
 +
 Not all pattern types support patterns containing \0. Git will error
@@ -279,44 +279,44 @@
 more search backends, until then we'll die when the pattern type in
 question doesn't support them.
 
--e::
+`-e`::
 	The next parameter is the pattern. This option has to be
 	used for patterns starting with `-` and should be used in
 	scripts passing user input to grep.  Multiple patterns are
-	combined by 'or'.
+	combined by `or`.
 
---and::
---or::
---not::
-( ... )::
+`--and`::
+`--or`::
+`--not`::
+`( ... )`::
 	Specify how multiple patterns are combined using Boolean
 	expressions.  `--or` is the default operator.  `--and` has
 	higher precedence than `--or`.  `-e` has to be used for all
 	patterns.
 
---all-match::
+`--all-match`::
 	When giving multiple pattern expressions combined with `--or`,
 	this flag is specified to limit the match to files that
 	have lines to match all of them.
 
--q::
---quiet::
+`-q`::
+`--quiet`::
 	Do not output matched lines; instead, exit with status 0 when
 	there is a match and with non-zero status when there isn't.
 
-<tree>...::
+`<tree>...`::
 	Instead of searching tracked files in the working tree, search
 	blobs in the given trees.
 
-\--::
+`--`::
 	Signals the end of options; the rest of the parameters
-	are <pathspec> limiters.
+	are _<pathspec>_ limiters.
 
-<pathspec>...::
+`<pathspec>...`::
 	If given, limit the search to paths matching at least one pattern.
-	Both leading paths match and glob(7) patterns are supported.
+	Both leading paths match and `glob`(7) patterns are supported.
 +
-For more details about the <pathspec> syntax, see the 'pathspec' entry
+For more details about the _<pathspec>_ syntax, see the `pathspec` entry
 in linkgit:gitglossary[7].
 
 EXAMPLES
diff --git a/Documentation/git-history.adoc b/Documentation/git-history.adoc
index 24dc907..2ba8121 100644
--- a/Documentation/git-history.adoc
+++ b/Documentation/git-history.adoc
@@ -8,6 +8,7 @@
 SYNOPSIS
 --------
 [synopsis]
+git history fixup <commit> [--dry-run] [--update-refs=(branches|head)] [--reedit-message] [--empty=(drop|keep|abort)]
 git history reword <commit> [--dry-run] [--update-refs=(branches|head)]
 git history split <commit> [--dry-run] [--update-refs=(branches|head)] [--] [<pathspec>...]
 
@@ -22,8 +23,9 @@
 This command is related to linkgit:git-rebase[1] in that both commands can be
 used to rewrite history. There are a couple of major differences though:
 
-* linkgit:git-history[1] can work in a bare repository as it does not need to
-  touch either the index or the worktree.
+* Most subcommands of linkgit:git-history[1] can work in a bare repository as
+  they do not need to touch either the index or the worktree. The `fixup`
+  subcommand is an exception to this, as it reads staged changes from the index.
 * linkgit:git-history[1] does not execute any linkgit:githooks[5] at the
   current point in time. This may change in the future.
 * linkgit:git-history[1] by default updates all branches that are descendants
@@ -48,11 +50,28 @@
 be stateful operations. The limitation can be lifted once (if) Git learns about
 first-class conflicts.
 
+When using `fixup` with `--empty=drop`, dropping the root commit is not yet
+supported.
+
 COMMANDS
 --------
 
 The following commands are available to rewrite history in different ways:
 
+`fixup <commit>`::
+	Apply the currently staged changes to the specified commit. This is
+	similar in nature to `git commit --fixup=<commit>` followed by `git
+	rebase --autosquash <commit>~`. Changes are applied to the target
+	commit by performing a three-way merge between the HEAD commit, the
+	target commit and the tree generated from staged changes.
++
+The commit message and authorship of the target commit are preserved by
+default, unless you specify `--reedit-message`.
++
+If applying the staged changes would result in a conflict, the command
+aborts with an error. All branches that are descendants of the original
+commit are updated to point to the rewritten history.
+
 `reword <commit>`::
 	Rewrite the commit message of the specified commit. All the other
 	details of this commit remain unchanged. This command will spawn an
@@ -87,6 +106,31 @@
 	objects will be written into the repository, so applying these printed
 	ref updates is generally safe.
 
+`--reedit-message`::
+	Open an editor to modify the target commit's message.
+
+`--empty=(drop|keep|abort)`::
+	Control what happens when a commit becomes empty as a result of the
+	fixup. This can happen in two situations:
++
+--
+* The fixup target itself becomes empty because the staged changes exactly
+  cancel out all changes introduced by that commit.
+
+* A descendant commit becomes empty during replay because it introduced the
+  same change that was just fixed up into an ancestor.
+--
++
+With `drop` (the default), empty commits are removed from the rewritten
+history. Descendants of a dropped target commit are replayed directly onto
+the target's parent. Note that dropping the root commit is not supported;
+see LIMITATIONS.
++
+With `keep`, empty commits are retained in the rewritten history as-is.
++
+With `abort`, the command stops with an error if any commit would become
+empty.
+
 `--update-refs=(branches|head)`::
 	Control which references will be updated by the command, if any. With
 	`branches`, all local branches that point to commits which are
@@ -96,6 +140,36 @@
 EXAMPLES
 --------
 
+Fixup a commit
+~~~~~~~~~~~~~~
+
+----------
+$ git log --oneline --stat
+abc1234 (HEAD -> main) third
+ third.txt | 1 +
+def5678 second
+ second.txt | 1 +
+ghi9012 first
+ first.txt | 1 +
+
+$ echo "change" >>unrelated.txt
+$ git add unrelated.txt
+$ git history fixup ghi9012
+
+$ git log --oneline --stat
+jkl3456 (HEAD -> main) third
+ third.txt | 1 +
+mno7890 second
+ second.txt | 1 +
+pqr1234 first
+ first.txt     | 1 +
+ unrelated.txt | 1 +
+----------
+
+The staged addition of `unrelated.txt` has been incorporated into the `first`
+commit. All descendant commits have been replayed on top of the rewritten
+history.
+
 Split a commit
 ~~~~~~~~~~~~~~
 
diff --git a/Documentation/git-hook.adoc b/Documentation/git-hook.adoc
index 318c637..4868852 100644
--- a/Documentation/git-hook.adoc
+++ b/Documentation/git-hook.adoc
@@ -3,19 +3,20 @@
 
 NAME
 ----
-git-hook - Run git hooks
+git-hook - Run Git hooks
 
 SYNOPSIS
 --------
 [verse]
-'git hook' run [--allow-unknown-hook-name] [--ignore-missing] [--to-stdin=<path>] <hook-name> [-- <hook-args>]
+'git hook' run [--allow-unknown-hook-name] [--ignore-missing] [--to-stdin=<path>] [(-j|--jobs) <n>]
+	<hook-name> [-- <hook-args>]
 'git hook' list [--allow-unknown-hook-name] [-z] [--show-scope] <hook-name>
 
 DESCRIPTION
 -----------
 
-A command interface for running git hooks (see linkgit:githooks[5]),
-for use by other scripted git commands.
+A command interface for running Git hooks (see linkgit:githooks[5]),
+for use by other scripted Git commands.
 
 This command parses the default configuration files for sets of configs like
 so:
@@ -40,7 +41,7 @@
 
 With this config, when you run 'git commit', first `~/bin/linter --cpp20` will
 have a chance to check your files to be committed (during the `pre-commit` hook
-event`), and then `~/bin/spellchecker` will have a chance to check your commit
+event), and then `~/bin/spellchecker` will have a chance to check your commit
 message (during the `commit-msg` hook event).
 
 Commands are run in the order Git encounters their associated
@@ -147,6 +148,23 @@
 	mirroring the output style of `git config --show-scope`. Traditional
 	hooks from the hookdir are unaffected.
 
+-j::
+--jobs::
+	Only valid for `run`.
++
+Specify how many hooks to run simultaneously. If this flag is not specified,
+the value of the `hook.jobs` config is used, see linkgit:git-config[1]. If
+neither is specified, defaults to 1 (serial execution).
++
+When greater than 1, it overrides the per-hook `hook.<friendly-name>.parallel`
+setting, allowing all hooks for the event to run concurrently, even if they
+are not individually marked as parallel.
++
+Some hooks always run sequentially regardless of this flag or the
+`hook.jobs` config, because Git knows they cannot safely run in parallel:
+`applypatch-msg`, `pre-commit`, `prepare-commit-msg`, `commit-msg`,
+`post-commit`, `post-checkout`, and `push-to-checkout`.
+
 WRAPPERS
 --------
 
@@ -169,7 +187,8 @@
 git hook run --allow-unknown-hook-name mywrapper-start-tests \
   # providing something to stdin
   --stdin some-tempfile-123 \
-  # execute hooks in serial
+  # execute multiple hooks in parallel
+  --jobs 3 \
   # plus some arguments of your own...
   -- \
   --testname bar \
@@ -185,6 +204,7 @@
 
 CONFIGURATION
 -------------
+:git-hook: 1
 include::config/hook.adoc[]
 
 SEE ALSO
diff --git a/Documentation/git-imap-send.adoc b/Documentation/git-imap-send.adoc
index 278e5cc..538b91a 100644
--- a/Documentation/git-imap-send.adoc
+++ b/Documentation/git-imap-send.adoc
@@ -8,9 +8,9 @@
 
 SYNOPSIS
 --------
-[verse]
-'git imap-send' [-v] [-q] [--[no-]curl] [(--folder|-f) <folder>]
-'git imap-send' --list
+[synopsis]
+git imap-send [-v] [-q] [--[no-]curl] [(--folder|-f) <folder>]
+git imap-send --list
 
 
 DESCRIPTION
@@ -32,30 +32,30 @@
 OPTIONS
 -------
 
--v::
---verbose::
+`-v`::
+`--verbose`::
 	Be verbose.
 
--q::
---quiet::
+`-q`::
+`--quiet`::
 	Be quiet.
 
--f <folder>::
---folder=<folder>::
+`-f <folder>`::
+`--folder=<folder>`::
 	Specify the folder in which the emails have to saved.
 	For example: `--folder=[Gmail]/Drafts` or `-f INBOX/Drafts`.
 
---curl::
+`--curl`::
 	Use libcurl to communicate with the IMAP server, unless tunneling
 	into it.  Ignored if Git was built without the USE_CURL_FOR_IMAP_SEND
 	option set.
 
---no-curl::
+`--no-curl`::
 	Talk to the IMAP server using git's own IMAP routines instead of
 	using libcurl.  Ignored if Git was built with the NO_OPENSSL option
 	set.
 
---list::
+`--list`::
 	Run the IMAP LIST command to output a list of all the folders present.
 
 CONFIGURATION
diff --git a/Documentation/git-log.adoc b/Documentation/git-log.adoc
index e304739..fb3ac11 100644
--- a/Documentation/git-log.adoc
+++ b/Documentation/git-log.adoc
@@ -36,14 +36,14 @@
 	Print out the ref names of any commits that are shown. Possible values
 	are:
 +
-----
+--
 `short`;; the ref name prefixes `refs/heads/`, `refs/tags/` and
 	`refs/remotes/` are not printed.
 `full`;; the full ref name (including prefix) is printed.
-`auto`:: if the output is going to a terminal, the ref names
+`auto`;; if the output is going to a terminal, the ref names
 	are shown as if `short` were given, otherwise no ref names are
 	shown.
-----
+--
 +
 The option `--decorate` is short-hand for `--decorate=short`. Default to
 configuration value of `log.decorate` if configured, otherwise, `auto`.
diff --git a/Documentation/git-ls-files.adoc b/Documentation/git-ls-files.adoc
index 58c529a..2b17538 100644
--- a/Documentation/git-ls-files.adoc
+++ b/Documentation/git-ls-files.adoc
@@ -331,7 +331,7 @@
   1. The file specified by the `core.excludesfile` configuration
      variable, if exists, or the `$XDG_CONFIG_HOME/git/ignore` file.
 
-  2. The `$GIT_DIR/info/exclude` file.
+  2. The `$GIT_COMMON_DIR/info/exclude` file.
 
 via the `--exclude-from=` option.
 
diff --git a/Documentation/git-multi-pack-index.adoc b/Documentation/git-multi-pack-index.adoc
index 6125683..c6d23ae 100644
--- a/Documentation/git-multi-pack-index.adoc
+++ b/Documentation/git-multi-pack-index.adoc
@@ -11,9 +11,11 @@
 [verse]
 'git multi-pack-index' [<options>] write [--preferred-pack=<pack>]
 		         [--[no-]bitmap] [--[no-]incremental] [--[no-]stdin-packs]
-		         [--refs-snapshot=<path>]
+		         [--refs-snapshot=<path>] [--[no-]write-chain-file]
+			 [--base=<checksum>]
 'git multi-pack-index' [<options>] compact [--[no-]incremental]
-		         [--[no-]bitmap] <from> <to>
+		         [--[no-]bitmap] [--base=<checksum>] [--[no-]write-chain-file]
+			 <from> <to>
 'git multi-pack-index' [<options>] verify
 'git multi-pack-index' [<options>] expire
 'git multi-pack-index' [<options>] repack [--batch-size=<size>]
@@ -83,6 +85,20 @@
 		and packs not present in an existing MIDX layer.
 		Migrates non-incremental MIDXs to incremental ones when
 		necessary.
+
+	--[no-]write-chain-file::
+		When used with `--incremental`, write a new MIDX layer
+		but do not update the multi-pack-index-chain file.
+		The checksum of the new layer is printed to standard
+		output, allowing the caller to assemble and write the
+		chain itself. Requires `--incremental`.
+
+	--base=<checksum>::
+		Specify the checksum of an existing MIDX layer to use
+		as the base when writing a new incremental layer.
+		The special value `none` indicates that the new layer
+		should have no base (i.e., it becomes a root layer).
+		Requires `--no-write-chain-file`.
 --
 
 compact::
@@ -97,7 +113,22 @@
 
 	--[no-]bitmap::
 		Control whether or not a multi-pack bitmap is written.
+
+	--[no-]write-chain-file::
+		When used with `--incremental`, write a new compacted
+		MIDX layer but do not update the multi-pack-index-chain
+		file. The checksum of the new layer is printed to
+		standard output. Requires `--incremental`.
+
+	--base=<checksum>::
+		Specify the checksum of an existing MIDX layer to use
+		as the base for the compacted result, instead of using
+		the immediate parent of `<from>`. The special value
+		`none` indicates that the result should have no base.
 --
++
+Note that the compact command requires writing a version-2 midx that
+cannot be read by versions of Git prior to v2.54.
 
 verify::
 	Verify the contents of the MIDX file.
diff --git a/Documentation/git-pack-objects.adoc b/Documentation/git-pack-objects.adoc
index 71b9682..8a27aa1 100644
--- a/Documentation/git-pack-objects.adoc
+++ b/Documentation/git-pack-objects.adoc
@@ -94,13 +94,24 @@
 	included packs (those not beginning with `^`), excluding any
 	objects listed in the excluded packs (beginning with `^`).
 +
-When `mode` is "follow", objects from packs not listed on stdin receive
-special treatment. Objects within unlisted packs will be included if
-those objects are (1) reachable from the included packs, and (2) not
-found in any excluded packs. This mode is useful, for example, to
-resurrect once-unreachable objects found in cruft packs to generate
-packs which are closed under reachability up to the boundary set by the
-excluded packs.
+When `mode` is "follow" packs may additionally be prefixed with `!`,
+indicating that they are excluded but not necessarily closed under
+reachability.  In addition to objects in included packs, the resulting
+pack may include additional objects based on the following:
++
+--
+* If any packs are marked with `!`, then objects reachable from such
+  packs or included ones via objects outside of excluded-closed packs
+  will be included. In this case, all `^` packs are treated as closed
+  under reachability.
+* Otherwise (if there are no `!` packs), objects within unlisted packs
+  will be included if those objects are (1) reachable from the
+  included packs, and (2) not found in any excluded packs.
+--
++
+This mode is useful, for example, to resurrect once-unreachable
+objects found in cruft packs to generate packs which are closed under
+reachability up to the boundary set by the excluded packs.
 +
 Incompatible with `--revs`, or options that imply `--revs` (such as
 `--all`), with the exception of `--unpacked`, which is compatible.
@@ -391,9 +402,11 @@
 	of filenames that cause collisions in Git's default name-hash
 	algorithm.
 +
-Incompatible with `--delta-islands`, `--shallow`, or `--filter`. The
-`--use-bitmap-index` option will be ignored in the presence of
-`--path-walk.`
+Incompatible with `--delta-islands`. The `--use-bitmap-index` option is
+ignored in the presence of `--path-walk`. The `--path-walk` option
+supports the `--filter=<spec>` forms `blob:none`, `blob:limit=<n>`,
+`tree:0`, `object:type=<type>`, and `sparse:<oid>`. These supported filter
+types can be combined with the `combine:<spec>+<spec>` form.
 
 
 DELTA ISLANDS
diff --git a/Documentation/git-range-diff.adoc b/Documentation/git-range-diff.adoc
index b5e85d3..5cc5e2e 100644
--- a/Documentation/git-range-diff.adoc
+++ b/Documentation/git-range-diff.adoc
@@ -7,11 +7,11 @@
 
 SYNOPSIS
 --------
-[verse]
-'git range-diff' [--color=[<when>]] [--no-color] [<diff-options>]
+[synopsis]
+git range-diff [--color=[<when>]] [--no-color] [<diff-options>]
 	[--no-dual-color] [--creation-factor=<factor>]
 	[--left-only | --right-only] [--diff-merges=<format>]
-	[--remerge-diff]
+	[--remerge-diff] [--no-notes | --notes[=<ref>]]
 	( <range1> <range2> | <rev1>...<rev2> | <base> <rev1> <rev2> )
 	[[--] <path>...]
 
@@ -21,14 +21,14 @@
 This command shows the differences between two versions of a patch
 series, or more generally, two commit ranges (ignoring merge commits).
 
-In the presence of `<path>` arguments, these commit ranges are limited
+In the presence of _<path>_ arguments, these commit ranges are limited
 accordingly.
 
 To that end, it first finds pairs of commits from both commit ranges
 that correspond with each other. Two commits are said to correspond when
 the diff between their patches (i.e. the author information, the commit
 message and the commit diff) is reasonably small compared to the
-patches' size. See ``Algorithm`` below for details.
+patches' size. See 'Algorithm' below for details.
 
 Finally, the list of matching commits is shown in the order of the
 second commit range, with unmatched commits being inserted just after
@@ -37,7 +37,7 @@
 There are three ways to specify the commit ranges:
 
 - `<range1> <range2>`: Either commit range can be of the form
-  `<base>..<rev>`, `<rev>^!` or `<rev>^-<n>`. See `SPECIFYING RANGES`
+  `<base>..<rev>`, `<rev>^!` or `<rev>^-<n>`. See 'SPECIFYING RANGES'
   in linkgit:gitrevisions[7] for more details.
 
 - `<rev1>...<rev2>`. This is equivalent to
@@ -48,7 +48,7 @@
 
 OPTIONS
 -------
---no-dual-color::
+`--no-dual-color`::
 	When the commit diffs differ, `git range-diff` recreates the
 	original diffs' coloring, and adds outer -/+ diff markers with
 	the *background* being red/green to make it easier to see e.g.
@@ -56,33 +56,33 @@
 +
 Additionally, the commit diff lines that are only present in the first commit
 range are shown "dimmed" (this can be overridden using the `color.diff.<slot>`
-config setting where `<slot>` is one of `contextDimmed`, `oldDimmed` and
+config setting where _<slot>_ is one of `contextDimmed`, `oldDimmed` and
 `newDimmed`), and the commit diff lines that are only present in the second
 commit range are shown in bold (which can be overridden using the config
-settings `color.diff.<slot>` with `<slot>` being one of `contextBold`,
+settings `color.diff.<slot>` with _<slot>_ being one of `contextBold`,
 `oldBold` or `newBold`).
 +
 This is known to `range-diff` as "dual coloring". Use `--no-dual-color`
 to revert to color all lines according to the outer diff markers
 (and completely ignore the inner diff when it comes to color).
 
---creation-factor=<percent>::
-	Set the creation/deletion cost fudge factor to `<percent>`.
+`--creation-factor=<percent>`::
+	Set the creation/deletion cost fudge factor to _<percent>_.
 	Defaults to 60. Try a larger value if `git range-diff` erroneously
 	considers a large change a total rewrite (deletion of one commit
 	and addition of another), and a smaller one in the reverse case.
-	See the ``Algorithm`` section below for an explanation of why this is
+	See the 'Algorithm' section below for an explanation of why this is
 	needed.
 
---left-only::
+`--left-only`::
 	Suppress commits that are missing from the first specified range
-	(or the "left range" when using the `<rev1>...<rev2>` format).
+	(or the "left range" when using the `<rev1>...<rev2>` form).
 
---right-only::
+`--right-only`::
 	Suppress commits that are missing from the second specified range
-	(or the "right range" when using the `<rev1>...<rev2>` format).
+	(or the "right range" when using the `<rev1>...<rev2>` form).
 
---diff-merges=<format>::
+`--diff-merges=<format>`::
 	Instead of ignoring merge commits, generate diffs for them using the
 	corresponding `--diff-merges=<format>` option of linkgit:git-log[1],
 	and include them in the comparison.
@@ -93,30 +93,30 @@
 non-conflicting `git merge`, the `remerge` mode will represent it with an empty
 diff.
 
---remerge-diff::
+`--remerge-diff`::
 	Convenience option, equivalent to `--diff-merges=remerge`.
 
---notes[=<ref>]::
---no-notes::
+`--notes[=<ref>]`::
+`--no-notes`::
 	This flag is passed to the `git log` program
 	(see linkgit:git-log[1]) that generates the patches.
 
-<range1> <range2>::
+`<range1> <range2>`::
 	Compare the commits specified by the two ranges, where
-	`<range1>` is considered an older version of `<range2>`.
+	_<range1>_ is considered an older version of _<range2>_.
 
-<rev1>...<rev2>::
+`<rev1>...<rev2>`::
 	Equivalent to passing `<rev2>..<rev1>` and `<rev1>..<rev2>`.
 
-<base> <rev1> <rev2>::
+`<base> <rev1> <rev2>`::
 	Equivalent to passing `<base>..<rev1>` and `<base>..<rev2>`.
-	Note that `<base>` does not need to be the exact branch point
+	Note that _<base>_ does not need to be the exact branch point
 	of the branches. Example: after rebasing a branch `my-topic`,
 	`git range-diff my-topic@{u} my-topic@{1} my-topic` would
 	show the differences introduced by the rebase.
 
 `git range-diff` also accepts the regular diff options (see
-linkgit:git-diff[1]), most notably the `--color=[<when>]` and
+linkgit:git-diff[1]), most notably the `--color[=<when>]` and
 `--no-color` options. These options are used when generating the "diff
 between patches", i.e. to compare the author, commit message and diff of
 corresponding old/new commits. There is currently no means to tweak most of the
diff --git a/Documentation/git-repack.adoc b/Documentation/git-repack.adoc
index 673ce91..72c4201 100644
--- a/Documentation/git-repack.adoc
+++ b/Documentation/git-repack.adoc
@@ -11,7 +11,7 @@
 [verse]
 'git repack' [-a] [-A] [-d] [-f] [-F] [-l] [-n] [-q] [-b] [-m]
 	[--window=<n>] [--depth=<n>] [--threads=<n>] [--keep-pack=<pack-name>]
-	[--write-midx] [--name-hash-version=<n>] [--path-walk]
+	[--write-midx[=<mode>]] [--name-hash-version=<n>] [--path-walk]
 
 DESCRIPTION
 -----------
@@ -250,9 +250,47 @@
 linkgit:git-multi-pack-index[1]).
 
 -m::
---write-midx::
+--write-midx[=<mode>]::
 	Write a multi-pack index (see linkgit:git-multi-pack-index[1])
-	containing the non-redundant packs.
+	containing the non-redundant packs. The following modes are
+	available:
++
+--
+	`default`;;
+		Write a single MIDX covering all packs. This is the
+		default when `--write-midx` is given without an
+		explicit mode.
+
+	`incremental`;;
+		Write an incremental MIDX chain instead of a single
+		flat MIDX.
++
+Without `--geometric`, a new MIDX layer is appended to the existing
+chain (or a new chain is started) containing whatever packs were written
+by the repack. Existing layers are preserved as-is.
++
+When combined with `--geometric`, the incremental mode maintains a chain
+of MIDX layers that is compacted over time using a geometric merging
+strategy. Each repack creates a new tip layer containing the newly
+written pack(s). Adjacent layers are then merged whenever the newer
+layer's object count exceeds `1/repack.midxSplitFactor` of the next
+deeper layer's count. Layers that do not meet this condition are
+retained as-is.
++
+The result is that newer (tip) layers tend to contain many small packs
+with relatively few objects, while older (deeper) layers contain fewer,
+larger packs covering more objects. Because compaction is driven by the
+tip of the chain, newer layers are also rewritten more frequently than
+older ones, which are only touched when enough objects have accumulated
+to justify merging into them. This keeps the total number of layers
+logarithmic relative to the total number of objects.
++
+Only packs in the tip MIDX layer are considered as candidates for the
+geometric repack; packs in deeper layers are left untouched. If the tip
+layer contains fewer packs than `repack.midxNewLayerThreshold`, those
+packs are excluded from the geometry entirely, and a new layer is
+created for any new pack(s) without disturbing the existing chain.
+--
 
 --name-hash-version=<n>::
 	Provide this argument to the underlying `git pack-objects` process.
diff --git a/Documentation/git-replace.adoc b/Documentation/git-replace.adoc
index 0a65460..436a0e5 100644
--- a/Documentation/git-replace.adoc
+++ b/Documentation/git-replace.adoc
@@ -145,6 +145,13 @@
 There may be other problems when using 'git rev-list' related to
 pending objects.
 
+CAVEATS
+-------
+
+The existence of replace objects or commit grafts turns off reading or
+writing to the commit-graph, which can cause performance issues. See
+linkgit:git-commit-graph[1].
+
 SEE ALSO
 --------
 linkgit:git-hash-object[1]
diff --git a/Documentation/git-replay.adoc b/Documentation/git-replay.adoc
index 997097e..a32f72a 100644
--- a/Documentation/git-replay.adoc
+++ b/Documentation/git-replay.adoc
@@ -9,7 +9,8 @@
 SYNOPSIS
 --------
 [verse]
-(EXPERIMENTAL!) 'git replay' ([--contained] --onto <newbase> | --advance <branch> | --revert <branch>) [--ref-action[=<mode>]] <revision-range>
+(EXPERIMENTAL!) 'git replay' ([--contained] --onto=<newbase> | --advance=<branch> | --revert=<branch>)
+			     [--ref=<ref>] [--ref-action=<mode>] <revision-range>
 
 DESCRIPTION
 -----------
@@ -26,7 +27,7 @@
 OPTIONS
 -------
 
---onto <newbase>::
+--onto=<newbase>::
 	Starting point at which to create the new commits.  May be any
 	valid commit, and not just an existing branch name.
 +
@@ -34,7 +35,7 @@
 updated to point at the new commits, similar to the way `git rebase --update-refs`
 updates multiple branches in the affected range.
 
---advance <branch>::
+--advance=<branch>::
 	Starting point at which to create the new commits; must be a
 	branch name.
 +
@@ -42,7 +43,7 @@
 point at the tip of the resulting history. This is different from `--onto`,
 which uses the target only as a starting point without updating it.
 
---revert <branch>::
+--revert=<branch>::
 	Starting point at which to create the reverted commits; must be a
 	branch name.
 +
@@ -65,6 +66,16 @@
 	Update all branches that point at commits in
 	<revision-range>. Requires `--onto`.
 
+--ref=<ref>::
+	Override which reference is updated with the result of the replay.
+	The ref must be fully qualified.
+	When used with `--onto`, the `<revision-range>` should have a
+	single tip and only the specified reference is updated instead of
+	inferring refs from the revision range.
+	When used with `--advance` or `--revert`, the specified reference is
+	updated instead of the branch given to those options.
+	This option is incompatible with `--contained`.
+
 --ref-action[=<mode>]::
 	Control how references are updated. The mode can be:
 +
@@ -79,8 +90,8 @@
 
 <revision-range>::
 	Range of commits to replay; see "Specifying Ranges" in
-	linkgit:git-rev-parse[1]. In `--advance <branch>` or
-	`--revert <branch>` mode, the range should have a single tip,
+	linkgit:git-rev-parse[1]. In `--advance=<branch>` or
+	`--revert=<branch>` mode, the range should have a single tip,
 	so that it's clear to which tip the advanced or reverted
 	<branch> should point. Any commits in the range whose changes
 	are already present in the branch the commits are being
@@ -127,7 +138,7 @@
 To simply rebase `mybranch` onto `target`:
 
 ------------
-$ git replay --onto target origin/main..mybranch
+$ git replay --onto=target origin/main..mybranch
 ------------
 
 The refs are updated atomically and no output is produced on success.
@@ -135,14 +146,14 @@
 To see what would be updated without actually updating:
 
 ------------
-$ git replay --ref-action=print --onto target origin/main..mybranch
+$ git replay --ref-action=print --onto=target origin/main..mybranch
 update refs/heads/mybranch ${NEW_mybranch_HASH} ${OLD_mybranch_HASH}
 ------------
 
 To cherry-pick the commits from mybranch onto target:
 
 ------------
-$ git replay --advance target origin/main..mybranch
+$ git replay --advance=target origin/main..mybranch
 ------------
 
 Note that the first two examples replay the exact same commits and on
@@ -154,7 +165,7 @@
 you'd really like to rebase the whole set?
 
 ------------
-$ git replay --contained --onto origin/main origin/main..tipbranch
+$ git replay --contained --onto=origin/main origin/main..tipbranch
 ------------
 
 All three branches (`branch1`, `branch2`, and `tipbranch`) are updated
@@ -165,7 +176,7 @@
 do:
 
 ------------
-$ git replay --onto origin/main ^base branch1 branch2 branch3
+$ git replay --onto=origin/main ^base branch1 branch2 branch3
 ------------
 
 This will simultaneously rebase `branch1`, `branch2`, and `branch3`,
@@ -176,7 +187,7 @@
 To revert commits on a branch:
 
 ------------
-$ git replay --revert main topic~2..topic
+$ git replay --revert=main topic~2..topic
 ------------
 
 This reverts the last two commits from `topic`, creating revert commits on
@@ -188,6 +199,16 @@
 commit-by-commit), consider using `git merge-tree --merge-base $TIP HEAD $BASE`
 which can avoid unnecessary merge conflicts.
 
+To replay onto a specific commit while updating a different reference:
+
+------------
+$ git replay --onto=112233 --ref=refs/heads/mybranch aabbcc..ddeeff
+------------
+
+This replays the range `aabbcc..ddeeff` onto commit `112233` and updates
+`refs/heads/mybranch` to point at the result. This can be useful when you want
+to use bare commit IDs instead of branch names.
+
 GIT
 ---
 Part of the linkgit:git[1] suite
diff --git a/Documentation/git-rerere.adoc b/Documentation/git-rerere.adoc
index 99f967b..4e6ab9a 100644
--- a/Documentation/git-rerere.adoc
+++ b/Documentation/git-rerere.adoc
@@ -44,7 +44,7 @@
 'forget' <pathspec>::
 
 Reset the conflict resolutions which rerere has recorded for the current
-conflict in paths that match <pathspec>.
+conflicts in paths that match <pathspec>.
 
 'diff'::
 
diff --git a/Documentation/git-restore.adoc b/Documentation/git-restore.adoc
index 961eef0..7d19423 100644
--- a/Documentation/git-restore.adoc
+++ b/Documentation/git-restore.adoc
@@ -41,7 +41,7 @@
 +
 As a special case, you may use `"<rev-A>...<rev-B>"` as a shortcut for the
 merge base of _<rev-A>_ and _<rev-B>_ if there is exactly one merge base. You can
-leave out at most one of _<rev-A>__ and _<rev-B>_, in which case it defaults to `HEAD`.
+leave out at most one of _<rev-A>_ and _<rev-B>_, in which case it defaults to `HEAD`.
 
 `-p`::
 `--patch`::
diff --git a/Documentation/git-shortlog.adoc b/Documentation/git-shortlog.adoc
index a11b57c..e067d39 100644
--- a/Documentation/git-shortlog.adoc
+++ b/Documentation/git-shortlog.adoc
@@ -3,63 +3,63 @@
 
 NAME
 ----
-git-shortlog - Summarize 'git log' output
+git-shortlog - Summarize `git log` output
 
 SYNOPSIS
 --------
-[verse]
-'git shortlog' [<options>] [<revision-range>] [[--] <path>...]
-git log --pretty=short | 'git shortlog' [<options>]
+[synopsis]
+git shortlog [<options>] [<revision-range>] [[--] <path>...]
+git log --pretty=short | git shortlog [<options>]
 
 DESCRIPTION
 -----------
-Summarizes 'git log' output in a format suitable for inclusion
+Summarizes `git log` output in a format suitable for inclusion
 in release announcements. Each commit will be grouped by author and title.
 
 Additionally, "[PATCH]" will be stripped from the commit description.
 
 If no revisions are passed on the command line and either standard input
-is not a terminal or there is no current branch, 'git shortlog' will
+is not a terminal or there is no current branch, `git shortlog` will
 output a summary of the log read from standard input, without
 reference to the current repository.
 
 OPTIONS
 -------
 
--n::
---numbered::
+`-n`::
+`--numbered`::
 	Sort output according to the number of commits per author instead
 	of author alphabetic order.
 
--s::
---summary::
+`-s`::
+`--summary`::
 	Suppress commit description and provide a commit count summary only.
 
--e::
---email::
+`-e`::
+`--email`::
 	Show the email address of each author.
 
---format[=<format>]::
+`--format[=<format>]`::
 	Instead of the commit subject, use some other information to
-	describe each commit.  '<format>' can be any string accepted
-	by the `--format` option of 'git log', such as '* [%h] %s'.
-	(See the "PRETTY FORMATS" section of linkgit:git-log[1].)
+	describe each commit. _<format>_ can be any string accepted
+	by the `--format` option of `git log`, such as '* [%h] %s'.
+	(See the 'PRETTY FORMATS' section of linkgit:git-log[1].)
 +
 Each pretty-printed commit will be rewrapped before it is shown.
 
---date=<format>::
+`--date=<format>`::
 	Show dates formatted according to the given date string. (See
-	the `--date` option in the "Commit Formatting" section of
+	the `--date` option in the 'Commit Formatting' section of
 	linkgit:git-log[1]). Useful with `--group=format:<format>`.
 
---group=<type>::
-	Group commits based on `<type>`. If no `--group` option is
-	specified, the default is `author`. `<type>` is one of:
+`--group=<type>`::
+	Group commits based on _<type>_. If no `--group` option is
+	specified, the default is `author`. _<type>_ is one of:
 +
 --
  - `author`, commits are grouped by author
  - `committer`, commits are grouped by committer (the same as `-c`)
- - `trailer:<field>`, the `<field>` is interpreted as a case-insensitive
+ - `trailer:<field>`, the _<field>_ is interpreted as a case-insensitive
    commit message trailer (see linkgit:git-interpret-trailers[1]). For
    example, if your project uses `Reviewed-by` trailers, you might want
    to see who has been reviewing with
@@ -76,7 +76,7 @@
 as an identity, it will be taken literally and completely.
 
  - `format:<format>`, any string accepted by the `--format` option of
-   'git log'. (See the "PRETTY FORMATS" section of
+   `git log`. (See the 'PRETTY FORMATS' section of
    linkgit:git-log[1].)
 --
 +
@@ -85,11 +85,11 @@
 example, `git shortlog --group=author --group=trailer:co-authored-by`
 counts both authors and co-authors.
 
--c::
---committer::
+`-c`::
+`--committer`::
 	This is an alias for `--group=committer`.
 
--w[<width>[,<indent1>[,<indent2>]]]::
+`-w[<width>[,<indent1>[,<indent2>]]]`::
 	Linewrap the output by wrapping each line at `width`.  The first
 	line of each entry is indented by `indent1` spaces, and the second
 	and subsequent lines are indented by `indent2` spaces. `width`,
@@ -98,16 +98,16 @@
 If width is `0` (zero) then indent the lines of the output without wrapping
 them.
 
-<revision-range>::
+`<revision-range>`::
 	Show only commits in the specified revision range.  When no
-	<revision-range> is specified, it defaults to `HEAD` (i.e. the
+	_<revision-range>_ is specified, it defaults to `HEAD` (i.e. the
 	whole history leading to the current commit).  `origin..HEAD`
 	specifies all the commits reachable from the current commit
 	(i.e. `HEAD`), but not from `origin`. For a complete list of
-	ways to spell <revision-range>, see the "Specifying Ranges"
+	ways to spell _<revision-range>_, see the 'Specifying Ranges'
 	section of linkgit:gitrevisions[7].
 
-[--] <path>...::
+`[--] <path>...`::
 	Consider only commits that are enough to explain how the files
 	that match the specified paths came to be.
 +
diff --git a/Documentation/git-stash.adoc b/Documentation/git-stash.adoc
index 235d57d..50bb89f 100644
--- a/Documentation/git-stash.adoc
+++ b/Documentation/git-stash.adoc
@@ -12,12 +12,12 @@
 git stash show [-u | --include-untracked | --only-untracked] [<diff-options>] [<stash>]
 git stash drop [-q | --quiet] [<stash>]
 git stash pop [--index] [-q | --quiet] [<stash>]
-git stash apply [--index] [-q | --quiet] [<stash>]
+git stash apply [--index] [-q | --quiet] [--label-ours=<label>] [--label-theirs=<label>] [--label-base=<label>] [<stash>]
 git stash branch <branchname> [<stash>]
-git stash [push [-p | --patch] [-S | --staged] [-k | --[no-]keep-index] [-q | --quiet]
+git stash [push] [-p | --patch] [-S | --staged] [-k | --[no-]keep-index] [-q | --quiet]
 	     [-u | --include-untracked] [-a | --all] [(-m | --message) <message>]
 	     [--pathspec-from-file=<file> [--pathspec-file-nul]]
-	     [--] [<pathspec>...]]
+	     [--] [<pathspec>...]
 git stash save [-p | --patch] [-S | --staged] [-k | --[no-]keep-index] [-q | --quiet]
            [-u | --include-untracked] [-a | --all] [<message>]
 git stash clear
@@ -60,10 +60,8 @@
 	the description along with the stashed state.
 +
 For quickly making a snapshot, you can omit "push".  In this mode,
-non-option arguments are not allowed to prevent a misspelled
-subcommand from making an unwanted stash entry.  The two exceptions to this
-are `stash -p` which acts as alias for `stash push -p` and pathspec elements,
-which are allowed after a double hyphen `--` for disambiguation.
+pathspec elements are only allowed after a double hyphen `--`
+to prevent a misspelled subcommand from making an unwanted stash entry.
 
 `save [-p | --patch] [-S | --staged] [-k | --[no-]keep-index] [-u | --include-untracked] [-a | --all] [-q | --quiet] [<message>]`::
 
@@ -197,6 +195,15 @@
 (which are stored in the index, where you therefore can no longer
 apply the changes as they were originally).
 
+`--label-ours=<label>`::
+`--label-theirs=<label>`::
+`--label-base=<label>`::
+	These options are only valid for the `apply` command.
++
+Use the given labels in conflict markers instead of the default
+"Updated upstream", "Stashed changes", and "Stash base".
+`--label-base` only has an effect with merge.conflictStyle=diff3.
+
 `-k`::
 `--keep-index`::
 `--no-keep-index`::
diff --git a/Documentation/git-svn.adoc b/Documentation/git-svn.adoc
index c26c12b..2a7fa60 100644
--- a/Documentation/git-svn.adoc
+++ b/Documentation/git-svn.adoc
@@ -439,7 +439,7 @@
 'show-ignore'::
 	Recursively finds and lists the svn:ignore and svn:global-ignores
 	properties on directories. The output is suitable for appending to
-	the $GIT_DIR/info/exclude file.
+	the $GIT_COMMON_DIR/info/exclude file.
 
 'mkdirs'::
 	Attempts to recreate empty directories that core Git cannot track
diff --git a/Documentation/git-switch.adoc b/Documentation/git-switch.adoc
index 87707e9..d6c4f22 100644
--- a/Documentation/git-switch.adoc
+++ b/Documentation/git-switch.adoc
@@ -123,18 +123,19 @@
 
 `-m`::
 `--merge`::
-	If you have local modifications to one or more files that are
-	different between the current branch and the branch to which
-	you are switching, the command refuses to switch branches in
-	order to preserve your modifications in context.  However,
-	with this option, a three-way merge between the current
-	branch, your working tree contents, and the new branch is
-	done, and you will be on the new branch.
-+
-When a merge conflict happens, the index entries for conflicting
-paths are left unmerged, and you need to resolve the conflicts
-and mark the resolved paths with `git add` (or `git rm` if the merge
-should result in deletion of the path).
+	If you have local modifications to one or more files that
+	are different between the current branch and the branch to
+	which you are switching, the command normally refuses to
+	switch branches in order to preserve your modifications in
+	context.  However, with this option, the conflicting local
+	changes are automatically stashed before the switch and
+	reapplied afterwards.  If the local changes do not overlap
+	with the differences between branches, the switch proceeds
+	without stashing.  If reapplying the stash results in
+	conflicts, the entry is saved to the stash list.  Resolve
+	the conflicts and run `git stash drop` when done, or clear
+	the working tree (e.g. with `git reset --hard`) before
+	running `git stash pop` later to re-apply your changes.
 
 `--conflict=<style>`::
 	The same as `--merge` option above, but changes the way the
@@ -217,15 +218,18 @@
 error: You have local changes to 'frotz'; not switching branches.
 ------------
 
-You can give the `-m` flag to the command, which would try a three-way
-merge:
+You can give the `-m` flag to the command, which will carry your local
+changes to the new branch:
 
 ------------
 $ git switch -m mytopic
-Auto-merging frotz
+Applied autostash.
+Switched to branch 'mytopic'
+The following paths have local changes:
+M	frotz
 ------------
 
-After this three-way merge, the local modifications are _not_
+After the switch, the local modifications are reapplied and are _not_
 registered in your index file, so `git diff` would show you what
 changes you made since the tip of the new branch.
 
diff --git a/Documentation/git-url-parse.adoc b/Documentation/git-url-parse.adoc
new file mode 100644
index 0000000..9d0d93d
--- /dev/null
+++ b/Documentation/git-url-parse.adoc
@@ -0,0 +1,80 @@
+git-url-parse(1)
+================
+
+NAME
+----
+git-url-parse - Parse and extract git URL components
+
+SYNOPSIS
+--------
+[synopsis]
+git url-parse [-c <component>] [--] <url>...
+
+DESCRIPTION
+-----------
+
+Git supports many ways to specify URLs, some of them non-standard.
+For example, git supports the scp style [user@]host:[path] format.
+This command eases interoperability with git URLs by enabling the
+parsing and extraction of the components of all git URLs.
+
+Any syntactically valid URL is parsed, even if the scheme is not one
+git supports for fetching or pushing.
+
+OPTIONS
+-------
+
+`-c <component>`::
+`--component <component>`::
+	Extract the _<component>_ component from the given Git URLs.
+	_<component>_ can be one of:
+	`scheme`, `user`, `password`, `host`, `port`, `path`.
+
+OUTPUT
+------
+
+When `--component` is given, the requested component of each URL
+is printed on its own line, in the order the URLs were given. If
+the URL has no such component (for example, a port in a URL that
+does not specify one), an empty line is printed in its place.
+
+When `--component` is not given, no output is produced. The exit
+status is zero if every URL parses successfully and non-zero
+otherwise, allowing the command to be used purely as a validator.
+
+EXAMPLES
+--------
+
+* Print the host name:
++
+------------
+$ git url-parse --component host https://example.com/user/repo
+example.com
+------------
+
+* Print the path:
++
+------------
+$ git url-parse --component path https://example.com/user/repo
+/user/repo
+$ git url-parse --component path example.com:~user/repo
+~user/repo
+$ git url-parse --component path example.com:user/repo
+/user/repo
+------------
+
+* Validate URLs without outputting anything:
++
+------------
+$ git url-parse https://example.com/user/repo example.com:~user/repo
+------------
+
+SEE ALSO
+--------
+linkgit:git-clone[1],
+linkgit:git-fetch[1],
+linkgit:git-config[1]
+
+GIT
+---
+Part of the linkgit:git[1] suite
diff --git a/Documentation/git-var.adoc b/Documentation/git-var.adoc
index b606c2d..697c10a 100644
--- a/Documentation/git-var.adoc
+++ b/Documentation/git-var.adoc
@@ -22,7 +22,7 @@
 	Display the logical variables. In addition, all the
 	variables of the Git configuration file .git/config are listed
 	as well. (However, the configuration variables listing functionality
-	is deprecated in favor of `git config -l`.)
+	is deprecated in favor of `git config list`.)
 
 EXAMPLES
 --------
diff --git a/Documentation/gitattributes.adoc b/Documentation/gitattributes.adoc
index f20041a..bd76167 100644
--- a/Documentation/gitattributes.adoc
+++ b/Documentation/gitattributes.adoc
@@ -911,7 +911,8 @@
 
 - `rust` suitable for source code in the Rust language.
 
-- `scheme` suitable for source code in the Scheme language.
+- `scheme` suitable for source code in most Lisp dialects,
+  including Scheme, Emacs Lisp, Common Lisp, and Clojure.
 
 - `tex` suitable for source code for LaTeX documents.
 
diff --git a/Documentation/gitcvs-migration.adoc b/Documentation/gitcvs-migration.adoc
index 1cd1283..905d08c 100644
--- a/Documentation/gitcvs-migration.adoc
+++ b/Documentation/gitcvs-migration.adoc
@@ -49,8 +49,7 @@
 ================================
 The 'pull' command knows where to get updates from because of certain
 configuration variables that were set by the first 'git clone'
-command; see `git config -l` and the linkgit:git-config[1] man
-page for details.
+command; see the subcommand `list` in linkgit:git-config[1] for details.
 ================================
 
 You can update the shared repository with your changes by first committing
diff --git a/Documentation/gitformat-index.adoc b/Documentation/gitformat-index.adoc
index 145cace..f6a427c 100644
--- a/Documentation/gitformat-index.adoc
+++ b/Documentation/gitformat-index.adoc
@@ -291,14 +291,14 @@
     sequence in variable width encoding. Each string describes the
     environment where the cache can be used.
 
-  - Stat data of $GIT_DIR/info/exclude. See "Index entry" section from
+  - Stat data of $GIT_COMMON_DIR/info/exclude. See "Index entry" section from
     ctime field until "file size".
 
   - Stat data of core.excludesFile
 
   - 32-bit dir_flags (see struct dir_struct)
 
-  - Hash of $GIT_DIR/info/exclude. A null hash means the file
+  - Hash of $GIT_COMMON_DIR/info/exclude. A null hash means the file
     does not exist.
 
   - Hash of core.excludesFile. A null hash means the file does
diff --git a/Documentation/gitignore.adoc b/Documentation/gitignore.adoc
index 9fccab4..7979e50 100644
--- a/Documentation/gitignore.adoc
+++ b/Documentation/gitignore.adoc
@@ -7,7 +7,7 @@
 
 SYNOPSIS
 --------
-$XDG_CONFIG_HOME/git/ignore, $GIT_DIR/info/exclude, .gitignore
+$XDG_CONFIG_HOME/git/ignore, $GIT_COMMON_DIR/info/exclude, .gitignore
 
 DESCRIPTION
 -----------
@@ -34,7 +34,7 @@
    includes such `.gitignore` files in its repository, containing patterns for
    files generated as part of the project build.
 
- * Patterns read from `$GIT_DIR/info/exclude`.
+ * Patterns read from `$GIT_COMMON_DIR/info/exclude`.
 
  * Patterns read from the file specified by the configuration
    variable `core.excludesFile`.
@@ -50,7 +50,7 @@
    specific to a particular repository but which do not need to be shared
    with other related repositories (e.g., auxiliary files that live inside
    the repository but are specific to one user's workflow) should go into
-   the `$GIT_DIR/info/exclude` file.
+   the `$GIT_COMMON_DIR/info/exclude` file.
 
  * Patterns which a user wants Git to
    ignore in all situations (e.g., backup or temporary files generated by
@@ -96,6 +96,11 @@
    particular `.gitignore` file itself. Otherwise the pattern may also
    match at any level below the `.gitignore` level.
 
+ - Patterns read from exclude sources that are outside the working tree,
+   such as $GIT_COMMON_DIR/info/exclude and core.excludesFile, are treated as if
+   they are specified at the root of the working tree, i.e. a leading "/"
+   in such patterns anchors the match at the root of the repository.
+
  - If there is a separator at the end of the pattern then the pattern
    will only match directories, otherwise the pattern can match both
    files and directories.
@@ -141,8 +146,8 @@
 
 The optional configuration variable `core.excludesFile` indicates a path to a
 file containing patterns of file names to exclude, similar to
-`$GIT_DIR/info/exclude`.  Patterns in the exclude file are used in addition to
-those in `$GIT_DIR/info/exclude`.
+`$GIT_COMMON_DIR/info/exclude`. Patterns in the exclude file are used in
+addition to those in `$GIT_COMMON_DIR/info/exclude`.
 
 NOTES
 -----
diff --git a/Documentation/gitmodules.adoc b/Documentation/gitmodules.adoc
index 3792da9..fd96639 100644
--- a/Documentation/gitmodules.adoc
+++ b/Documentation/gitmodules.adoc
@@ -70,10 +70,10 @@
 --
 	all;; The submodule will never be considered modified (but will
 	    nonetheless show up in the output of status and commit when it has
-	    been staged). Add `(new commits)` can be overruled using the
-	    `git add --force <submodule.path>`.
-		The setting affects `status`, `update-index`, `diff` and `log`(due
-		to underlaying `diff`).
+	    been staged).  Adding a submodule with `(new commits)` can be
+	    overridden using `git add --force <submodule.path>`.
+	    This setting affects `status`, `update-index`, `diff` and `log`
+	    (due to underlying `diff`).
 
 	dirty;; All changes to the submodule's work tree will be ignored, only
 	    committed differences between the `HEAD` of the submodule and its
diff --git a/Documentation/gitpacking.adoc b/Documentation/gitpacking.adoc
index a56596e..e6de6ec 100644
--- a/Documentation/gitpacking.adoc
+++ b/Documentation/gitpacking.adoc
@@ -150,7 +150,7 @@
 	pattern = "refs/"
 	threshold = now
 	stableThreshold = never
-	sampleRate = 100
+	sampleRate = 1
 	maxMerges = 64
 ----
 
@@ -177,7 +177,7 @@
 	pattern = "refs/virtual/([0-9]+)/(heads|tags)/"
 	threshold = now
 	stableThreshold = never
-	sampleRate = 100
+	sampleRate = 1
 	maxMerges = 64
 ----
 
diff --git a/Documentation/gitprotocol-v2.adoc b/Documentation/gitprotocol-v2.adoc
index f985cb4..befa697 100644
--- a/Documentation/gitprotocol-v2.adoc
+++ b/Documentation/gitprotocol-v2.adoc
@@ -659,7 +659,7 @@
 applicable.
 +
 The advertised URI may alternatively contain a plaintext file that `git
-config --list` would accept (with the `--file` option). The key-value
+config list` would accept (with the `--file` option). The key-value
 pairs in this list are in the `bundle.*` namespace (see
 linkgit:git-config[1]).
 
@@ -848,6 +848,10 @@
 where `pr-name` is the urlencoded name of a promisor remote the server
 advertised and the client accepts.
 
+The promisor remotes that the client accepted will be tried before the
+other configured promisor remotes when the client attempts to fetch
+missing objects.
+
 Note that, everywhere in this document, the ';' and ',' characters
 MUST be encoded if they appear in `pr-name` or `field-value`.
 
diff --git a/Documentation/gittutorial.adoc b/Documentation/gittutorial.adoc
index f89ad30..519b8d8 100644
--- a/Documentation/gittutorial.adoc
+++ b/Documentation/gittutorial.adoc
@@ -432,7 +432,7 @@
 -------------------------------------
 
 (The complete configuration created by `git clone` is visible using
-`git config -l`, and the linkgit:git-config[1] man page
+`git config list`, and the linkgit:git-config[1] man page
 explains the meaning of each option.)
 
 Git also keeps a pristine copy of Alice's `master` branch under the
diff --git a/Documentation/glossary-content.adoc b/Documentation/glossary-content.adoc
index 20ba121..8c4e9dd 100644
--- a/Documentation/glossary-content.adoc
+++ b/Documentation/glossary-content.adoc
@@ -430,6 +430,7 @@
    matches "`a/b`", "`a/x/b`", "`a/x/y/b`" and so on.
 
  - Other consecutive asterisks are considered invalid.
+
 +
 Glob magic is incompatible with literal magic.
 
@@ -452,6 +453,7 @@
 
 - "`!ATTR`" requires that the attribute `ATTR` be
   unspecified.
+
 +
 Note that when matching against a tree object, attributes are still
 obtained from working tree, not from the given tree object.
@@ -560,14 +562,17 @@
 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:
 +
+--
  - 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`
@@ -577,6 +582,7 @@
  - `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
diff --git a/Documentation/line-range-options.adoc b/Documentation/line-range-options.adoc
index c44ba05..ecb2c79 100644
--- a/Documentation/line-range-options.adoc
+++ b/Documentation/line-range-options.adoc
@@ -12,4 +12,8 @@
 	(namely `--raw`, `--numstat`, `--shortstat`, `--dirstat`, `--summary`,
 	`--name-only`, `--name-status`, `--check`) are not currently implemented.
 +
+Patch formatting options such as `--word-diff`, `--color-moved`,
+`--no-prefix`, and whitespace options (`-w`, `-b`) are supported,
+as are pickaxe options (`-S`, `-G`).
++
 include::line-range-format.adoc[]
diff --git a/Documentation/meson.build b/Documentation/meson.build
index d6365b8..f4854f8 100644
--- a/Documentation/meson.build
+++ b/Documentation/meson.build
@@ -55,6 +55,7 @@
   'git-for-each-ref.adoc' : 1,
   'git-for-each-repo.adoc' : 1,
   'git-format-patch.adoc' : 1,
+  'git-format-rev.adoc' : 1,
   'git-fsck-objects.adoc' : 1,
   'git-fsck.adoc' : 1,
   'git-fsmonitor--daemon.adoc' : 1,
@@ -155,6 +156,7 @@
   'git-update-server-info.adoc' : 1,
   'git-upload-archive.adoc' : 1,
   'git-upload-pack.adoc' : 1,
+  'git-url-parse.adoc' : 1,
   'git-var.adoc' : 1,
   'git-verify-commit.adoc' : 1,
   'git-verify-pack.adoc' : 1,
diff --git a/Documentation/ref-storage-format.adoc b/Documentation/ref-storage-format.adoc
index 6a8db47..c5e29ec 100644
--- a/Documentation/ref-storage-format.adoc
+++ b/Documentation/ref-storage-format.adoc
@@ -1,3 +1,8 @@
-`files`;; for loose files with packed-refs. This is the default.
-`reftable`;; for the reftable format. This format is experimental and its
-  internals are subject to change.
+`files`;; for loose files with packed-refs.
+ifndef::with-breaking-changes[]
+	This is the default.
+endif::with-breaking-changes[]
+`reftable`;; for the reftable format.
+ifdef::with-breaking-changes[]
+	This is the default.
+endif::with-breaking-changes[]
diff --git a/Documentation/rev-list-options.adoc b/Documentation/rev-list-options.adoc
index 2d195a1..9e666b9 100644
--- a/Documentation/rev-list-options.adoc
+++ b/Documentation/rev-list-options.adoc
@@ -23,7 +23,8 @@
 
 `--since=<date>`::
 `--after=<date>`::
-	Show commits more recent than _<date>_.
+	Show commits more recent than _<date>_.  As a special case,
+	'today' means the last midnight.
 
 `--since-as-filter=<date>`::
 	Show all commits more recent than _<date>_. This visits
@@ -1259,6 +1260,12 @@
 	in between them in that case. If _<barrier>_ is specified, it
 	is the string that will be shown instead of the default one.
 
+`--graph-lane-limit=<n>`::
+	When `--graph` is used, limit the number of graph lanes to be shown.
+	Lanes over the limit are replaced with a truncation mark '~'.
+	By default it is set to 0 (no limit), zero and negative values
+	are ignored and treated as no limit.
+
 ifdef::git-rev-list[]
 `--count`::
 	Print a number stating how many commits would have been
diff --git a/Documentation/technical/api-path-walk.adoc b/Documentation/technical/api-path-walk.adoc
index a67de1b..6e17b13 100644
--- a/Documentation/technical/api-path-walk.adoc
+++ b/Documentation/technical/api-path-walk.adoc
@@ -48,6 +48,13 @@
 	applications could disable some options to make it simpler to walk
 	the objects or to have fewer calls to `path_fn`.
 +
+Note that objects directly requested as pending objects (such as targets
+of lightweight tags or other ref tips) are always emitted to `path_fn`,
+even when the corresponding type flag is disabled. Only objects
+discovered during the tree walk are subject to these type filters. This
+ensures that objects specifically requested through the revision input
+are never silently dropped.
++
 While it is possible to walk only commits in this way, consumers would be
 better off using the revision walk API instead.
 
diff --git a/Documentation/technical/api-trace2.adoc b/Documentation/technical/api-trace2.adoc
index cf493da..918e517 100644
--- a/Documentation/technical/api-trace2.adoc
+++ b/Documentation/technical/api-trace2.adoc
@@ -1253,7 +1253,7 @@
 $ git config --system color.ui never
 $ git config --global color.ui always
 $ git config --local color.ui auto
-$ git config --list --show-scope | grep 'color.ui'
+$ git config list --show-scope | grep 'color.ui'
 system  color.ui=never
 global  color.ui=always
 local   color.ui=auto
diff --git a/Documentation/user-manual.adoc b/Documentation/user-manual.adoc
index 64009ba..5ec65ce 100644
--- a/Documentation/user-manual.adoc
+++ b/Documentation/user-manual.adoc
@@ -2865,7 +2865,7 @@
 linkgit:git-config[1]:
 
 -------------------------------------------------
-$ git config -l
+$ git config list
 core.repositoryformatversion=0
 core.filemode=true
 core.logallrefupdates=true
diff --git a/GIT-VERSION-GEN b/GIT-VERSION-GEN
index 92ea811..fd6979b 100755
--- a/GIT-VERSION-GEN
+++ b/GIT-VERSION-GEN
@@ -1,6 +1,6 @@
 #!/bin/sh
 
-DEF_VER=v2.54.0-rc0
+DEF_VER=v2.54.0
 
 LF='
 '
diff --git a/Makefile b/Makefile
index 5d22394..b31ecb0 100644
--- a/Makefile
+++ b/Makefile
@@ -283,13 +283,9 @@
 # Define SKIP_DASHED_BUILT_INS if you do not need the dashed versions of the
 # built-ins to be linked/copied at all.
 #
-# Define USE_NED_ALLOCATOR if you want to replace the platforms default
-# memory allocators with the nedmalloc allocator written by Niall Douglas.
-#
 # Define OVERRIDE_STRDUP to override the libc version of strdup(3).
 # This is necessary when using a custom allocator in order to avoid
 # crashes due to allocation and free working on different 'heaps'.
-# It's defined automatically if USE_NED_ALLOCATOR is set.
 #
 # Define NO_REGEX if your C library lacks regex support with REG_STARTEND
 # feature.
@@ -420,7 +416,7 @@
 # If your platform has OS-specific ways to tell if a repo is incompatible with
 # fsmonitor (whether the hook or IPC daemon version), set FSMONITOR_OS_SETTINGS
 # to the "<name>" of the corresponding `compat/fsmonitor/fsm-settings-<name>.c`
-# that implements the `fsm_os_settings__*()` routines.
+# and `compat/fsmonitor/fsm-ipc-<name>.c` files.
 #
 # Define LINK_FUZZ_PROGRAMS if you want `make all` to also build the fuzz test
 # programs in oss-fuzz/.
@@ -498,9 +494,9 @@
 #
 # == Optional Rust support ==
 #
-# Define WITH_RUST if you want to include features and subsystems written in
-# Rust into Git. For now, Rust is still an optional feature of the build
-# process. With Git 3.0 though, Rust will always be enabled.
+# Define NO_RUST if you want to disable features and subsystems written in Rust
+# from being compiled into Git. For now, Rust is still an optional feature of
+# the build process. With Git 3.0 though, Rust will always be enabled.
 #
 # Building Rust code requires Cargo.
 #
@@ -872,6 +868,7 @@
 TEST_BUILTINS_OBJS += test-submodule-nested-repo-config.o
 TEST_BUILTINS_OBJS += test-submodule.o
 TEST_BUILTINS_OBJS += test-subprocess.o
+TEST_BUILTINS_OBJS += test-synthesize.o
 TEST_BUILTINS_OBJS += test-trace2.o
 TEST_BUILTINS_OBJS += test-truncate.o
 TEST_BUILTINS_OBJS += test-userdiff.o
@@ -895,6 +892,7 @@
 BUILT_INS += git-cherry$X
 BUILT_INS += git-cherry-pick$X
 BUILT_INS += git-format-patch$X
+BUILT_INS += git-format-rev$X
 BUILT_INS += git-fsck-objects$X
 BUILT_INS += git-init$X
 BUILT_INS += git-maintenance$X
@@ -1218,7 +1216,9 @@
 LIB_OBJS += odb.o
 LIB_OBJS += odb/source.o
 LIB_OBJS += odb/source-files.o
+LIB_OBJS += odb/source-inmemory.o
 LIB_OBJS += odb/streaming.o
+LIB_OBJS += odb/transaction.o
 LIB_OBJS += oid-array.o
 LIB_OBJS += oidmap.o
 LIB_OBJS += oidset.o
@@ -1351,7 +1351,7 @@
 LIB_OBJS += usage.o
 LIB_OBJS += userdiff.o
 LIB_OBJS += utf8.o
-ifndef WITH_RUST
+ifdef NO_RUST
 LIB_OBJS += varint.o
 endif
 LIB_OBJS += version.o
@@ -1497,6 +1497,7 @@
 BUILTIN_OBJS += builtin/update-server-info.o
 BUILTIN_OBJS += builtin/upload-archive.o
 BUILTIN_OBJS += builtin/upload-pack.o
+BUILTIN_OBJS += builtin/url-parse.o
 BUILTIN_OBJS += builtin/var.o
 BUILTIN_OBJS += builtin/verify-commit.o
 BUILTIN_OBJS += builtin/verify-pack.o
@@ -1511,7 +1512,6 @@
 # upstream unnecessarily (making merging in future changes easier).
 THIRD_PARTY_SOURCES += compat/inet_ntop.c
 THIRD_PARTY_SOURCES += compat/inet_pton.c
-THIRD_PARTY_SOURCES += compat/nedmalloc/%
 THIRD_PARTY_SOURCES += compat/obstack.%
 THIRD_PARTY_SOURCES += compat/poll/%
 THIRD_PARTY_SOURCES += compat/regex/%
@@ -1527,6 +1527,7 @@
 CLAR_TEST_SUITES += u-hashmap
 CLAR_TEST_SUITES += u-list-objects-filter-options
 CLAR_TEST_SUITES += u-mem-pool
+CLAR_TEST_SUITES += u-odb-inmemory
 CLAR_TEST_SUITES += u-oid-array
 CLAR_TEST_SUITES += u-oidmap
 CLAR_TEST_SUITES += u-oidtree
@@ -1590,7 +1591,7 @@
 ALL_CFLAGS = $(DEVELOPER_CFLAGS) $(CPPFLAGS) $(CFLAGS) $(CFLAGS_APPEND)
 ALL_LDFLAGS = $(LDFLAGS) $(LDFLAGS_APPEND)
 
-ifdef WITH_RUST
+ifndef NO_RUST
 BASIC_CFLAGS += -DWITH_RUST
 GITLIBS += $(RUST_LIB)
 ifeq ($(uname_S),Windows)
@@ -2029,10 +2030,6 @@
 	COMPAT_CFLAGS += -DNO_PREAD
 	COMPAT_OBJS += compat/pread.o
 endif
-ifdef NO_WRITEV
-	COMPAT_CFLAGS += -DNO_WRITEV
-	COMPAT_OBJS += compat/writev.o
-endif
 ifdef NO_FAST_WORKING_DIRECTORY
 	BASIC_CFLAGS += -DNO_FAST_WORKING_DIRECTORY
 endif
@@ -2271,12 +2268,6 @@
 	BASIC_CFLAGS += -DNATIVE_CRLF
 endif
 
-ifdef USE_NED_ALLOCATOR
-	COMPAT_CFLAGS += -Icompat/nedmalloc
-	COMPAT_OBJS += compat/nedmalloc/nedmalloc.o
-	OVERRIDE_STRDUP = YesPlease
-endif
-
 ifdef OVERRIDE_STRDUP
 	COMPAT_CFLAGS += -DOVERRIDE_STRDUP
 	COMPAT_OBJS += compat/strdup.o
@@ -2383,13 +2374,13 @@
 	COMPAT_CFLAGS += -DHAVE_FSMONITOR_DAEMON_BACKEND
 	COMPAT_OBJS += compat/fsmonitor/fsm-listen-$(FSMONITOR_DAEMON_BACKEND).o
 	COMPAT_OBJS += compat/fsmonitor/fsm-health-$(FSMONITOR_DAEMON_BACKEND).o
-	COMPAT_OBJS += compat/fsmonitor/fsm-ipc-$(FSMONITOR_DAEMON_BACKEND).o
 endif
 
 ifdef FSMONITOR_OS_SETTINGS
 	COMPAT_CFLAGS += -DHAVE_FSMONITOR_OS_SETTINGS
+	COMPAT_OBJS += compat/fsmonitor/fsm-ipc-$(FSMONITOR_OS_SETTINGS).o
 	COMPAT_OBJS += compat/fsmonitor/fsm-settings-$(FSMONITOR_OS_SETTINGS).o
-	COMPAT_OBJS += compat/fsmonitor/fsm-path-utils-$(FSMONITOR_OS_SETTINGS).o
+	COMPAT_OBJS += compat/fsmonitor/fsm-path-utils-$(FSMONITOR_DAEMON_BACKEND).o
 endif
 
 ifdef WITH_BREAKING_CHANGES
@@ -2675,7 +2666,7 @@
 
 help.sp help.s help.o: command-list.h
 builtin/bugreport.sp builtin/bugreport.s builtin/bugreport.o: hook-list.h
-builtin/hook.sp builtin/hook.s builtin/hook.o: hook-list.h
+hook.sp hook.s hook.o: hook-list.h
 
 builtin/help.sp builtin/help.s builtin/help.o: config-list.h GIT-PREFIX
 builtin/help.sp builtin/help.s builtin/help.o: EXTRA_CPPFLAGS = \
@@ -2987,12 +2978,6 @@
 	-DGAWK -DNO_MBSUPPORT
 endif
 
-ifdef USE_NED_ALLOCATOR
-compat/nedmalloc/nedmalloc.sp compat/nedmalloc/nedmalloc.o: EXTRA_CPPFLAGS = \
-	-DNDEBUG -DREPLACE_SYSTEM_ALLOCATOR
-compat/nedmalloc/nedmalloc.sp: SP_EXTRA_FLAGS += -Wno-non-pointer-null
-endif
-
 headless-git.o: compat/win32/headless.c GIT-CFLAGS
 	$(QUIET_CC)$(CC) $(ALL_CFLAGS) $(COMPAT_CFLAGS) \
 		-fno-stack-protector -o $@ -c -Wall -Wwrite-strings $<
diff --git a/RelNotes b/RelNotes
index 84ef738..159e44a 120000
--- a/RelNotes
+++ b/RelNotes
@@ -1 +1 @@
-Documentation/RelNotes/2.54.0.adoc
\ No newline at end of file
+Documentation/RelNotes/2.55.0.adoc
\ No newline at end of file
diff --git a/add-patch.c b/add-patch.c
index 4e28e5c..f27edcb 100644
--- a/add-patch.c
+++ b/add-patch.c
@@ -558,8 +558,8 @@ static int parse_diff(struct add_p_state *s, const struct pathspec *ps)
 		strvec_push(&args,
 			    /* could be on an unborn branch */
 			    !strcmp("HEAD", s->revision) &&
-			    repo_get_oid(the_repository, "HEAD", &oid) ?
-			    empty_tree_oid_hex(the_repository->hash_algo) : s->revision);
+			    repo_get_oid(s->r, "HEAD", &oid) ?
+			    empty_tree_oid_hex(s->r->hash_algo) : s->revision);
 	}
 	color_arg_index = args.nr;
 	/* Use `--no-color` explicitly, just in case `diff.color = always`. */
@@ -1271,7 +1271,7 @@ static int edit_hunk_manually(struct add_p_state *s, struct hunk *hunk)
 				"removed, then the edit is\n"
 				"aborted and the hunk is left unchanged.\n"));
 
-	if (strbuf_edit_interactively(the_repository, &s->buf,
+	if (strbuf_edit_interactively(s->r, &s->buf,
 				      "addp-hunk-edit.diff", NULL) < 0)
 		return -1;
 
@@ -1679,7 +1679,7 @@ static size_t patch_update_file(struct add_p_state *s,
 		if (file_diff->hunk_nr) {
 			if (rendered_hunk_index != hunk_index) {
 				if (use_pager) {
-					setup_pager(the_repository);
+					setup_pager(s->r);
 					sigchain_push(SIGPIPE, SIG_IGN);
 				}
 				render_hunk(s, hunk, 0, colored, &s->buf);
diff --git a/alias.c b/alias.c
index ec9833d..e737c49 100644
--- a/alias.c
+++ b/alias.c
@@ -34,8 +34,20 @@ static int config_alias_cb(const char *var, const char *value,
 	if (subsection && !subsection_len)
 		subsection = NULL;
 
-	if (subsection && strcmp(key, "command"))
-		return 0;
+	if (subsection && strcmp(key, "command")) {
+		/*
+		 * We have historically supported the "alias.name" form when
+		 * "name" happens to contain dots (e.g., alias.foo.bar to allow
+		 * "git foo.bar". But our parsing above would split that into
+		 * subsection "foo".
+		 *
+		 * If we do not understand the final key in a subsection-style
+		 * variable, fall back to treating it as a two-level alias.
+		 */
+		key = var + strlen("alias.");
+		subsection = NULL;
+		subsection_len = 0;
+	}
 
 	if (data->alias) {
 		int match;
diff --git a/apply.c b/apply.c
index 63d5e3c..249248d 100644
--- a/apply.c
+++ b/apply.c
@@ -1840,8 +1840,16 @@ static int parse_fragment(struct apply_state *state,
 			trailing++;
 			check_old_for_crlf(patch, line, len);
 			if (!state->apply_in_reverse &&
-			    state->ws_error_action == correct_ws_error)
-				check_whitespace(state, line, len, patch->ws_rule);
+			    state->ws_error_action == correct_ws_error) {
+				const char *test_line = line;
+				int test_len = len;
+				if (*line == '\n') {
+					test_line = " \n";
+					test_len = 2;
+				}
+				check_whitespace(state, test_line, test_len,
+						 patch->ws_rule);
+			}
 			break;
 		case '-':
 			if (!state->apply_in_reverse)
@@ -4873,8 +4881,10 @@ static int apply_patch(struct apply_state *state,
 
 	state->patch_input_file = filename;
 	state->linenr = 1;
-	if (read_patch_file(&buf, fd) < 0)
-		return -128;
+	if (read_patch_file(&buf, fd) < 0) {
+		res = -128;
+		goto end;
+	}
 	offset = 0;
 	while (offset < buf.len) {
 		struct patch *patch;
diff --git a/archive.c b/archive.c
index fcd474c..5122910 100644
--- a/archive.c
+++ b/archive.c
@@ -786,7 +786,7 @@ int write_archive(int argc, const char **argv, const char *prefix,
 		 * die ourselves; but its error message will be more specific
 		 * than what we could write here.
 		 */
-		setup_git_directory();
+		setup_git_directory(the_repository);
 	}
 
 	parse_treeish_arg(argv, &args, remote);
diff --git a/bisect.c b/bisect.c
index ef17a44..905a9af 100644
--- a/bisect.c
+++ b/bisect.c
@@ -711,7 +711,7 @@ static enum bisect_error error_if_skipped_commits(struct commit_list *tried,
 		return BISECT_OK;
 
 	printf("There are only 'skip'ped commits left to test.\n"
-	       "The first %s commit could be any of:\n", term_bad);
+	       "The first '%s' commit could be any of:\n", term_bad);
 
 	for ( ; tried; tried = tried->next)
 		printf("%s\n", oid_to_hex(&tried->item->object.oid));
@@ -810,7 +810,7 @@ static enum bisect_error handle_bad_merge_base(void)
 				"between %s and [%s].\n"),
 				bad_hex, bad_hex, good_hex);
 		} else {
-			fprintf(stderr, _("The merge base %s is %s.\n"
+			fprintf(stderr, _("The merge base %s is '%s'.\n"
 				"This means the first '%s' commit is "
 				"between %s and [%s].\n"),
 				bad_hex, term_bad, term_good, bad_hex, good_hex);
@@ -820,9 +820,9 @@ static enum bisect_error handle_bad_merge_base(void)
 		return BISECT_MERGE_BASE_CHECK;
 	}
 
-	fprintf(stderr, _("Some %s revs are not ancestors of the %s rev.\n"
+	fprintf(stderr, _("Some '%s' revs are not ancestors of the '%s' rev.\n"
 		"git bisect cannot work properly in this case.\n"
-		"Maybe you mistook %s and %s revs?\n"),
+		"Maybe you mistook '%s' and '%s' revs?\n"),
 		term_good, term_bad, term_good, term_bad);
 	return BISECT_FAILED;
 }
@@ -835,7 +835,7 @@ static void handle_skipped_merge_base(const struct object_id *mb)
 
 	warning(_("the merge base between %s and [%s] "
 		"must be skipped.\n"
-		"So we cannot be sure the first %s commit is "
+		"So we cannot be sure the first '%s' commit is "
 		"between %s and %s.\n"
 		"We continue anyway."),
 		bad_hex, good_hex, term_bad, mb_hex, bad_hex);
@@ -928,7 +928,7 @@ static enum bisect_error check_good_are_ancestors_of_bad(struct repository *r,
 	struct commit **rev;
 
 	if (!current_bad_oid)
-		return error(_("a %s revision is needed"), term_bad);
+		return error(_("a '%s' revision is needed"), term_bad);
 
 	filename = repo_git_path(the_repository, "BISECT_ANCESTORS_OK");
 
@@ -1090,7 +1090,7 @@ enum bisect_error bisect_next_all(struct repository *r, const char *prefix)
 		res = error_if_skipped_commits(tried, NULL);
 		if (res < 0)
 			goto cleanup;
-		printf(_("%s was both %s and %s\n"),
+		printf(_("%s was both '%s' and '%s'\n"),
 		       oid_to_hex(current_bad_oid),
 		       term_good,
 		       term_bad);
@@ -1113,7 +1113,7 @@ enum bisect_error bisect_next_all(struct repository *r, const char *prefix)
 		res = error_if_skipped_commits(tried, current_bad_oid);
 		if (res)
 			goto cleanup;
-		printf("%s is the first %s commit\n", oid_to_hex(bisect_rev),
+		printf("%s is the first '%s' commit\n", oid_to_hex(bisect_rev),
 			term_bad);
 
 		show_commit(revs.commits->item);
diff --git a/blame.c b/blame.c
index a3c49d1..977cbb7 100644
--- a/blame.c
+++ b/blame.c
@@ -2813,7 +2813,7 @@ void setup_scoreboard(struct blame_scoreboard *sb,
 		}
 
 		if (!sb->contents_from)
-			setup_work_tree();
+			setup_work_tree(the_repository);
 
 		sb->final = fake_working_tree_commit(sb->repo,
 						     &sb->revs->diffopt,
diff --git a/builtin.h b/builtin.h
index 235c51f..4e47a4e 100644
--- a/builtin.h
+++ b/builtin.h
@@ -189,6 +189,7 @@ int cmd_fmt_merge_msg(int argc, const char **argv, const char *prefix, struct re
 int cmd_for_each_ref(int argc, const char **argv, const char *prefix, struct repository *repo);
 int cmd_for_each_repo(int argc, const char **argv, const char *prefix, struct repository *repo);
 int cmd_format_patch(int argc, const char **argv, const char *prefix, struct repository *repo);
+int cmd_format_rev(int argc, const char **argv, const char *prefix, struct repository *repo);
 int cmd_fsck(int argc, const char **argv, const char *prefix, struct repository *repo);
 int cmd_fsmonitor__daemon(int argc, const char **argv, const char *prefix, struct repository *repo);
 int cmd_gc(int argc, const char **argv, const char *prefix, struct repository *repo);
@@ -271,6 +272,7 @@ int cmd_update_server_info(int argc, const char **argv, const char *prefix, stru
 int cmd_upload_archive(int argc, const char **argv, const char *prefix, struct repository *repo);
 int cmd_upload_archive_writer(int argc, const char **argv, const char *prefix, struct repository *repo);
 int cmd_upload_pack(int argc, const char **argv, const char *prefix, struct repository *repo);
+int cmd_url_parse(int argc, const char **argv, const char *prefix, struct repository *repo);
 int cmd_var(int argc, const char **argv, const char *prefix, struct repository *repo);
 int cmd_verify_commit(int argc, const char **argv, const char *prefix, struct repository *repo);
 int cmd_verify_tag(int argc, const char **argv, const char *prefix, struct repository *repo);
diff --git a/builtin/add.c b/builtin/add.c
index 7737ab8..c859f66 100644
--- a/builtin/add.c
+++ b/builtin/add.c
@@ -16,6 +16,7 @@
 #include "run-command.h"
 #include "object-file.h"
 #include "odb.h"
+#include "odb/transaction.h"
 #include "parse-options.h"
 #include "path.h"
 #include "preload-index.h"
diff --git a/builtin/am.c b/builtin/am.c
index fe6e087..e9623b8 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -490,9 +490,11 @@ static int run_applypatch_msg_hook(struct am_state *state)
 
 	assert(state->msg);
 
-	if (!state->no_verify)
-		ret = run_hooks_l(the_repository, "applypatch-msg",
-				  am_path(state, "final-commit"), NULL);
+	if (!state->no_verify) {
+		struct run_hooks_opt opt = RUN_HOOKS_OPT_INIT_FORCE_SERIAL;
+		strvec_push(&opt.args, am_path(state, "final-commit"));
+		ret = run_hooks_opt(the_repository, "applypatch-msg", &opt);
+	}
 
 	if (!ret) {
 		FREE_AND_NULL(state->msg);
diff --git a/builtin/archive.c b/builtin/archive.c
index 13ea730..3c1288a 100644
--- a/builtin/archive.c
+++ b/builtin/archive.c
@@ -31,7 +31,7 @@ static int run_remote_archiver(int argc, const char **argv,
 
 	_remote = remote_get(remote);
 	transport = transport_get(_remote, _remote->url.v[0]);
-	transport_connect(transport, "git-upload-archive", exec, fd);
+	transport_connect(transport, GIT_CONNECT_UPLOAD_ARCHIVE, exec, fd);
 
 	/*
 	 * Inject a fake --format field at the beginning of the
diff --git a/builtin/backfill.c b/builtin/backfill.c
index 2c5ce56..e71e0f4 100644
--- a/builtin/backfill.c
+++ b/builtin/backfill.c
@@ -26,7 +26,7 @@
 #include "path-walk.h"
 
 static const char * const builtin_backfill_usage[] = {
-	N_("git backfill [--min-batch-size=<n>] [--[no-]sparse]"),
+	N_("git backfill [--min-batch-size=<n>] [--[no-]sparse] [--[no-]include-edges] [<revision-range>]"),
 	NULL
 };
 
@@ -35,6 +35,7 @@ struct backfill_context {
 	struct oid_array current_batch;
 	size_t min_batch_size;
 	int sparse;
+	int include_edges;
 	struct rev_info revs;
 };
 
@@ -78,6 +79,29 @@ static int fill_missing_blobs(const char *path UNUSED,
 	return 0;
 }
 
+static void reject_unsupported_rev_list_options(struct rev_info *revs)
+{
+	if (revs->diffopt.pickaxe)
+		die(_("'%s' cannot be used with 'git backfill'"),
+		    (revs->diffopt.pickaxe_opts & DIFF_PICKAXE_REGEX) ? "-G" : "-S");
+	if (revs->diffopt.filter || revs->diffopt.filter_not)
+		die(_("'%s' cannot be used with 'git backfill'"),
+		    "--diff-filter");
+	if (revs->diffopt.flags.follow_renames)
+		die(_("'%s' cannot be used with 'git backfill'"),
+		    "--follow");
+	if (revs->line_level_traverse)
+		die(_("'%s' cannot be used with 'git backfill'"),
+		    "-L");
+	if (revs->explicit_diff_merges)
+		die(_("'%s' cannot be used with 'git backfill'"),
+		    "--diff-merges");
+	if (!path_walk_filter_compatible(&revs->filter))
+		die(_("cannot backfill with these filter options"));
+	if (revs->filter.blob_limit_value)
+		die(_("cannot backfill with blob size limits"));
+}
+
 static int do_backfill(struct backfill_context *ctx)
 {
 	struct path_walk_info info = PATH_WALK_INFO_INIT;
@@ -85,6 +109,7 @@ static int do_backfill(struct backfill_context *ctx)
 
 	if (ctx->sparse) {
 		CALLOC_ARRAY(info.pl, 1);
+		info.pl_sparse_trees = 1;
 		if (get_sparse_checkout_patterns(info.pl)) {
 			path_walk_info_clear(&info);
 			return error(_("problem loading sparse-checkout"));
@@ -94,6 +119,8 @@ static int do_backfill(struct backfill_context *ctx)
 	/* Walk from HEAD if otherwise unspecified. */
 	if (!ctx->revs.pending.nr)
 		add_head_to_pending(&ctx->revs);
+	if (ctx->include_edges)
+		ctx->revs.edge_hint = 1;
 
 	info.blobs = 1;
 	info.tags = info.commits = info.trees = 0;
@@ -119,14 +146,17 @@ int cmd_backfill(int argc, const char **argv, const char *prefix, struct reposit
 		.repo = repo,
 		.current_batch = OID_ARRAY_INIT,
 		.min_batch_size = 50000,
-		.sparse = 0,
+		.sparse = -1,
 		.revs = REV_INFO_INIT,
+		.include_edges = 1,
 	};
 	struct option options[] = {
 		OPT_UNSIGNED(0, "min-batch-size", &ctx.min_batch_size,
 			     N_("Minimum number of objects to request at a time")),
 		OPT_BOOL(0, "sparse", &ctx.sparse,
 			 N_("Restrict the missing objects to the current sparse-checkout")),
+		OPT_BOOL(0, "include-edges", &ctx.include_edges,
+			 N_("Include blobs from boundary commits in the backfill")),
 		OPT_END(),
 	};
 	struct repo_config_values *cfg = repo_config_values(the_repository);
@@ -144,6 +174,7 @@ int cmd_backfill(int argc, const char **argv, const char *prefix, struct reposit
 
 	if (argc > 1)
 		die(_("unrecognized argument: %s"), argv[1]);
+	reject_unsupported_rev_list_options(&ctx.revs);
 
 	repo_config(repo, git_default_config, NULL);
 
diff --git a/builtin/bisect.c b/builtin/bisect.c
index 4520e58..606698b 100644
--- a/builtin/bisect.c
+++ b/builtin/bisect.c
@@ -465,13 +465,16 @@ static void bisect_print_status(const struct bisect_terms *terms)
 		return;
 
 	if (!state.nr_good && !state.nr_bad)
-		bisect_log_printf(_("status: waiting for both good and bad commits\n"));
+		bisect_log_printf(_("status: waiting for both '%s' and '%s' commits\n"),
+				  terms->term_good, terms->term_bad);
 	else if (state.nr_good)
-		bisect_log_printf(Q_("status: waiting for bad commit, %d good commit known\n",
-				     "status: waiting for bad commit, %d good commits known\n",
-				     state.nr_good), state.nr_good);
+		bisect_log_printf(Q_("status: waiting for '%s' commit, %d '%s' commit known\n",
+				     "status: waiting for '%s' commit, %d '%s' commits known\n",
+				     state.nr_good),
+				  terms->term_bad, state.nr_good, terms->term_good);
 	else
-		bisect_log_printf(_("status: waiting for good commit(s), bad commit known\n"));
+		bisect_log_printf(_("status: waiting for '%s' commit(s), '%s' commit known\n"),
+				  terms->term_good, terms->term_bad);
 }
 
 static int bisect_next_check(const struct bisect_terms *terms,
@@ -513,8 +516,8 @@ static int bisect_terms(struct bisect_terms *terms, const char *option)
 		return error(_("no terms defined"));
 
 	if (!option) {
-		printf(_("Your current terms are %s for the old state\n"
-			 "and %s for the new state.\n"),
+		printf(_("Your current terms are '%s' for the old state\n"
+			 "and '%s' for the new state.\n"),
 		       terms->term_good, terms->term_bad);
 		return 0;
 	}
@@ -632,7 +635,7 @@ static int bisect_skipped_commits(struct bisect_terms *terms)
 		strbuf_reset(&commit_name);
 		repo_format_commit_message(the_repository, commit, "%s",
 					   &commit_name, &pp);
-		fprintf(fp, "# possible first %s commit: [%s] %s\n",
+		fprintf(fp, "# possible first '%s' commit: [%s] %s\n",
 			terms->term_bad, oid_to_hex(&commit->object.oid),
 			commit_name.buf);
 	}
@@ -663,7 +666,7 @@ static int bisect_successful(struct bisect_terms *terms)
 	repo_format_commit_message(the_repository, commit, "%s", &commit_name,
 				   &pp);
 
-	res = append_to_file(git_path_bisect_log(), "# first %s commit: [%s] %s\n",
+	res = append_to_file(git_path_bisect_log(), "# first '%s' commit: [%s] %s\n",
 			    terms->term_bad, oid_to_hex(&commit->object.oid),
 			    commit_name.buf);
 
@@ -1262,14 +1265,14 @@ static int bisect_run(struct bisect_terms *terms, int argc, const char **argv)
 			int rc = verify_good(terms, command.buf);
 			is_first_run = 0;
 			if (rc < 0 || 128 <= rc) {
-				error(_("unable to verify %s on good"
-					" revision"), command.buf);
+				error(_("unable to verify %s on '%s' revision"),
+				      command.buf, terms->term_good);
 				res = BISECT_FAILED;
 				break;
 			}
 			if (rc == res) {
-				error(_("bogus exit code %d for good revision"),
-				      rc);
+				error(_("bogus exit code %d for '%s' revision"),
+				      rc, terms->term_good);
 				res = BISECT_FAILED;
 				break;
 			}
@@ -1314,7 +1317,7 @@ static int bisect_run(struct bisect_terms *terms, int argc, const char **argv)
 			puts(_("bisect run success"));
 			res = BISECT_OK;
 		} else if (res == BISECT_INTERNAL_SUCCESS_1ST_BAD_FOUND) {
-			puts(_("bisect found first bad commit"));
+			printf(_("bisect found first '%s' commit\n"), terms->term_bad);
 			res = BISECT_OK;
 		} else if (res) {
 			error(_("bisect run failed: 'git bisect %s'"
diff --git a/builtin/blame.c b/builtin/blame.c
index f3a11ef..ffbd3ce 100644
--- a/builtin/blame.c
+++ b/builtin/blame.c
@@ -708,7 +708,7 @@ static unsigned parse_score(const char *arg)
 
 static char *add_prefix(const char *prefix, const char *path)
 {
-	return prefix_path(prefix, prefix ? strlen(prefix) : 0, path);
+	return prefix_path(the_repository, prefix, prefix ? strlen(prefix) : 0, path);
 }
 
 static int git_blame_config(const char *var, const char *value,
diff --git a/builtin/cat-file.c b/builtin/cat-file.c
index b6f12f4..fa45f77 100644
--- a/builtin/cat-file.c
+++ b/builtin/cat-file.c
@@ -57,6 +57,20 @@ static int use_mailmap;
 
 static char *replace_idents_using_mailmap(char *, size_t *);
 
+/*
+ * The mailmap is initialized with .strdup_strings set to 0,
+ * but read_mailmap() sets the bit to 1 (this is true even when
+ * not a single mailmap entry is read), so it can be used for
+ * lazy loading.
+ */
+static void load_mailmap(void)
+{
+	if (mailmap.strdup_strings)
+		return;
+
+	read_mailmap(the_repository, &mailmap);
+}
+
 static char *replace_idents_using_mailmap(char *object_buf, size_t *size)
 {
 	struct strbuf sb = STRBUF_INIT;
@@ -161,7 +175,7 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name)
 
 	case 'e':
 		ret = !odb_has_object(the_repository->objects, &oid,
-				      HAS_OBJECT_RECHECK_PACKED | HAS_OBJECT_FETCH_PROMISOR);
+				      ODB_HAS_OBJECT_RECHECK_PACKED | ODB_HAS_OBJECT_FETCH_PROMISOR);
 		goto cleanup;
 
 	case 'w':
@@ -692,6 +706,20 @@ static void parse_cmd_info(struct batch_options *opt,
 	batch_one_object(line, output, opt, data);
 }
 
+static void parse_cmd_mailmap(struct batch_options *opt UNUSED,
+			      const char *line,
+			      struct strbuf *output UNUSED,
+			      struct expand_data *data UNUSED)
+{
+	use_mailmap = git_parse_maybe_bool(line);
+
+	if (use_mailmap < 0)
+		die(_("mailmap: invalid boolean '%s'"), line);
+
+	if (use_mailmap)
+		load_mailmap();
+}
+
 static void dispatch_calls(struct batch_options *opt,
 		struct strbuf *output,
 		struct expand_data *data,
@@ -725,9 +753,10 @@ static const struct parse_cmd {
 	parse_cmd_fn_t fn;
 	unsigned takes_args;
 } commands[] = {
-	{ "contents", parse_cmd_contents, 1},
-	{ "info", parse_cmd_info, 1},
-	{ "flush", NULL, 0},
+	{ "contents", parse_cmd_contents, 1 },
+	{ "info", parse_cmd_info, 1 },
+	{ "flush", NULL, 0 },
+	{ "mailmap", parse_cmd_mailmap, 1 },
 };
 
 static void batch_objects_command(struct batch_options *opt,
@@ -848,6 +877,9 @@ static void batch_each_object(struct batch_options *opt,
 		.callback = callback,
 		.payload = _payload,
 	};
+	struct odb_for_each_object_options opts = {
+		.flags = flags,
+	};
 	struct bitmap_index *bitmap = NULL;
 	struct odb_source *source;
 
@@ -860,7 +892,7 @@ static void batch_each_object(struct batch_options *opt,
 	odb_prepare_alternates(the_repository->objects);
 	for (source = the_repository->objects->sources; source; source = source->next) {
 		int ret = odb_source_loose_for_each_object(source, NULL, batch_one_object_oi,
-							   &payload, flags);
+							   &payload, &opts);
 		if (ret)
 			break;
 	}
@@ -884,7 +916,7 @@ static void batch_each_object(struct batch_options *opt,
 		for (source = the_repository->objects->sources; source; source = source->next) {
 			struct odb_source_files *files = odb_source_files_downcast(source);
 			int ret = packfile_store_for_each_object(files->packed, &oi,
-								 batch_one_object_oi, &payload, flags);
+								 batch_one_object_oi, &payload, &opts);
 			if (ret)
 				break;
 		}
@@ -1128,7 +1160,7 @@ int cmd_cat_file(int argc,
 	opt_epts = (opt == 'e' || opt == 'p' || opt == 't' || opt == 's');
 
 	if (use_mailmap)
-		read_mailmap(the_repository, &mailmap);
+		load_mailmap();
 
 	switch (batch.objects_filter.choice) {
 	case LOFC_DISABLED:
diff --git a/builtin/check-attr.c b/builtin/check-attr.c
index 51ed48c..98f64d5 100644
--- a/builtin/check-attr.c
+++ b/builtin/check-attr.c
@@ -67,7 +67,7 @@ static void check_attr(const char *prefix, struct attr_check *check,
 
 {
 	char *full_path =
-		prefix_path(prefix, prefix ? strlen(prefix) : 0, file);
+		prefix_path(the_repository, prefix, prefix ? strlen(prefix) : 0, file);
 
 	if (collect_all) {
 		git_all_attrs(the_repository->index, full_path, check);
@@ -117,7 +117,7 @@ int cmd_check_attr(int argc,
 	int cnt, i, doubledash, filei;
 
 	if (!is_bare_repository())
-		setup_work_tree();
+		setup_work_tree(the_repository);
 
 	repo_config(the_repository, git_default_config, NULL);
 
diff --git a/builtin/check-ref-format.c b/builtin/check-ref-format.c
index 5d80afe..e42b044 100644
--- a/builtin/check-ref-format.c
+++ b/builtin/check-ref-format.c
@@ -1,6 +1,9 @@
 /*
  * GIT - The information manager from hell
  */
+
+#define USE_THE_REPOSITORY_VARIABLE
+
 #include "builtin.h"
 #include "refs.h"
 #include "setup.h"
@@ -41,7 +44,7 @@ static int check_ref_format_branch(const char *arg)
 	const char *name;
 	int nongit;
 
-	setup_git_directory_gently(&nongit);
+	setup_git_directory_gently(the_repository, &nongit);
 	if (check_branch_ref(&sb, arg) ||
 	    !skip_prefix(sb.buf, "refs/heads/", &name))
 		die("'%s' is not a valid branch name", arg);
diff --git a/builtin/checkout-index.c b/builtin/checkout-index.c
index 188128a..311b94f 100644
--- a/builtin/checkout-index.c
+++ b/builtin/checkout-index.c
@@ -303,7 +303,7 @@ int cmd_checkout_index(int argc,
 			die("git checkout-index: don't mix '--all' and explicit filenames");
 		if (read_from_stdin)
 			die("git checkout-index: don't mix '--stdin' and explicit filenames");
-		p = prefix_path(prefix, prefix_length, arg);
+		p = prefix_path(repo, prefix, prefix_length, arg);
 		err |= checkout_file(repo->index, p, prefix);
 		free(p);
 	}
@@ -325,7 +325,7 @@ int cmd_checkout_index(int argc,
 					die("line is badly quoted");
 				strbuf_swap(&buf, &unquoted);
 			}
-			p = prefix_path(prefix, prefix_length, buf.buf);
+			p = prefix_path(repo, prefix, prefix_length, buf.buf);
 			err |= checkout_file(repo->index, p, prefix);
 			free(p);
 		}
diff --git a/builtin/checkout.c b/builtin/checkout.c
index e031e61..b78b3a1 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -17,7 +17,6 @@
 #include "merge-ll.h"
 #include "lockfile.h"
 #include "mem-pool.h"
-#include "merge-ort-wrappers.h"
 #include "object-file.h"
 #include "object-name.h"
 #include "odb.h"
@@ -30,7 +29,10 @@
 #include "repo-settings.h"
 #include "resolve-undo.h"
 #include "revision.h"
+#include "sequencer.h"
 #include "setup.h"
+#include "sparse-index.h"
+#include "strvec.h"
 #include "submodule.h"
 #include "symlinks.h"
 #include "trace2.h"
@@ -99,6 +101,8 @@ struct checkout_opts {
 	.auto_advance = 1, \
 }
 
+#define MERGE_WORKING_TREE_UNPACK_FAILED (-2)
+
 struct branch_info {
 	char *name; /* The short name used */
 	char *path; /* The full name of a real branch */
@@ -123,24 +127,80 @@ static void branch_info_release(struct branch_info *info)
 static int post_checkout_hook(struct commit *old_commit, struct commit *new_commit,
 			      int changed)
 {
-	return run_hooks_l(the_repository, "post-checkout",
-			   oid_to_hex(old_commit ? &old_commit->object.oid : null_oid(the_hash_algo)),
-			   oid_to_hex(new_commit ? &new_commit->object.oid : null_oid(the_hash_algo)),
-			   changed ? "1" : "0", NULL);
-	/* "new_commit" can be NULL when checking out from the index before
-	   a commit exists. */
+	struct run_hooks_opt opt = RUN_HOOKS_OPT_INIT_FORCE_SERIAL;
 
+	/*
+	 * "new_commit" can be NULL when checking out from the index before
+	 * a commit exists.
+	 */
+	strvec_pushl(&opt.args,
+		     oid_to_hex(old_commit ? &old_commit->object.oid : null_oid(the_hash_algo)),
+		     oid_to_hex(new_commit ? &new_commit->object.oid : null_oid(the_hash_algo)),
+		     changed ? "1" : "0",
+		     NULL);
+
+	return run_hooks_opt(the_repository, "post-checkout", &opt);
+}
+
+/*
+ * Handle a tree object and determine if we need to recurse into the
+ * tree (READ_TREE_RECURSIVE) or skip it (0).
+ */
+static int try_update_sparse_directory(const struct object_id *oid,
+				       struct strbuf *base,
+				       const char *pathname,
+				       int overlay_mode)
+{
+	struct strbuf dirpath = STRBUF_INIT;
+	struct cache_entry *old;
+	int pos, result = READ_TREE_RECURSIVE;
+
+	if (!the_repository->index->sparse_index)
+		return result;
+
+	strbuf_addbuf(&dirpath, base);
+	strbuf_addstr(&dirpath, pathname);
+	strbuf_addch(&dirpath, '/');
+
+	pos = index_name_pos_sparse(the_repository->index,
+				    dirpath.buf, dirpath.len);
+	if (pos < 0)
+		goto cleanup;
+
+	old = the_repository->index->cache[pos];
+	if (!S_ISSPARSEDIR(old->ce_mode))
+		goto cleanup;
+
+	if (oideq(oid, &old->oid)) {
+		/* Tree content already matches; no need to descend. */
+		result = 0;
+	} else if (!overlay_mode) {
+		/*
+		 * In non-overlay mode (e.g., restore --staged), replace the
+		 * sparse directory OID directly since files not present in
+		 * the source tree should be removed anyway.
+		 */
+		oidcpy(&old->oid, oid);
+		old->ce_flags |= CE_UPDATE;
+		result = 0;
+	}
+
+cleanup:
+	strbuf_release(&dirpath);
+	return result;
 }
 
 static int update_some(const struct object_id *oid, struct strbuf *base,
-		       const char *pathname, unsigned mode, void *context UNUSED)
+		       const char *pathname, unsigned mode, void *context)
 {
 	int len;
 	struct cache_entry *ce;
 	int pos;
+	int overlay_mode = context ? *((int *)context) : 1;
 
 	if (S_ISDIR(mode))
-		return READ_TREE_RECURSIVE;
+		return try_update_sparse_directory(oid, base, pathname,
+						   overlay_mode);
 
 	len = base->len + strlen(pathname);
 	ce = make_empty_cache_entry(the_repository->index, len);
@@ -156,7 +216,7 @@ static int update_some(const struct object_id *oid, struct strbuf *base,
 	 * entry in place. Whether it is UPTODATE or not, checkout_entry will
 	 * do the right thing.
 	 */
-	pos = index_name_pos(the_repository->index, ce->name, ce->ce_namelen);
+	pos = index_name_pos_sparse(the_repository->index, ce->name, ce->ce_namelen);
 	if (pos >= 0) {
 		struct cache_entry *old = the_repository->index->cache[pos];
 		if (ce->ce_mode == old->ce_mode &&
@@ -173,10 +233,11 @@ static int update_some(const struct object_id *oid, struct strbuf *base,
 	return 0;
 }
 
-static int read_tree_some(struct tree *tree, const struct pathspec *pathspec)
+static int read_tree_some(struct tree *tree, const struct pathspec *pathspec,
+			  int overlay_mode)
 {
 	read_tree(the_repository, tree,
-		  pathspec, update_some, NULL);
+		  pathspec, update_some, &overlay_mode);
 
 	/* update the index with the given tree's info
 	 * for all args, expanding wildcards, and exit
@@ -571,7 +632,8 @@ static int checkout_paths(const struct checkout_opts *opts,
 		return error(_("index file corrupt"));
 
 	if (opts->source_tree)
-		read_tree_some(opts->source_tree, &opts->pathspec);
+		read_tree_some(opts->source_tree, &opts->pathspec,
+			       opts->overlay_mode);
 	if (opts->merge)
 		unmerge_index(the_repository->index, &opts->pathspec, CE_MATCHED);
 
@@ -753,9 +815,9 @@ static void setup_branch_path(struct branch_info *branch)
 	branch->path = strbuf_detach(&buf, NULL);
 }
 
-static void init_topts(struct unpack_trees_options *topts, int merge,
+static void init_topts(struct unpack_trees_options *topts,
 		       int show_progress, int overwrite_ignore,
-		       struct commit *old_commit)
+		       bool quiet)
 {
 	memset(topts, 0, sizeof(*topts));
 	topts->head_idx = -1;
@@ -767,7 +829,7 @@ static void init_topts(struct unpack_trees_options *topts, int merge,
 	topts->initial_checkout = is_index_unborn(the_repository->index);
 	topts->update = 1;
 	topts->merge = 1;
-	topts->quiet = merge && old_commit;
+	topts->quiet = quiet;
 	topts->verbose_update = show_progress;
 	topts->fn = twoway_merge;
 	topts->preserve_ignored = !overwrite_ignore;
@@ -776,6 +838,7 @@ static void init_topts(struct unpack_trees_options *topts, int merge,
 static int merge_working_tree(const struct checkout_opts *opts,
 			      struct branch_info *old_branch_info,
 			      struct branch_info *new_branch_info,
+			      bool quiet,
 			      int *writeout_error)
 {
 	int ret;
@@ -783,8 +846,10 @@ static int merge_working_tree(const struct checkout_opts *opts,
 	struct tree *new_tree;
 
 	repo_hold_locked_index(the_repository, &lock_file, LOCK_DIE_ON_ERROR);
-	if (repo_read_index_preload(the_repository, NULL, 0) < 0)
+	if (repo_read_index_preload(the_repository, NULL, 0) < 0) {
+		rollback_lock_file(&lock_file);
 		return error(_("index file corrupt"));
+	}
 
 	resolve_undo_clear_index(the_repository->index);
 	if (opts->new_orphan_branch && opts->orphan_from_empty_tree) {
@@ -797,14 +862,18 @@ static int merge_working_tree(const struct checkout_opts *opts,
 	} else {
 		new_tree = repo_get_commit_tree(the_repository,
 						new_branch_info->commit);
-		if (!new_tree)
+		if (!new_tree) {
+			rollback_lock_file(&lock_file);
 			return error(_("unable to read tree (%s)"),
 				     oid_to_hex(&new_branch_info->commit->object.oid));
+		}
 	}
 	if (opts->discard_changes) {
 		ret = reset_tree(new_tree, opts, 1, writeout_error, new_branch_info);
-		if (ret)
+		if (ret) {
+			rollback_lock_file(&lock_file);
 			return ret;
+		}
 	} else {
 		struct tree_desc trees[2];
 		struct tree *tree;
@@ -814,13 +883,14 @@ static int merge_working_tree(const struct checkout_opts *opts,
 		refresh_index(the_repository->index, REFRESH_QUIET, NULL, NULL, NULL);
 
 		if (unmerged_index(the_repository->index)) {
+			rollback_lock_file(&lock_file);
 			error(_("you need to resolve your current index first"));
 			return 1;
 		}
 
 		/* 2-way merge to the new branch */
-		init_topts(&topts, opts->merge, opts->show_progress,
-			   opts->overwrite_ignore, old_branch_info->commit);
+		init_topts(&topts, opts->show_progress,
+			   opts->overwrite_ignore, quiet);
 		init_checkout_metadata(&topts.meta, new_branch_info->refname,
 				       new_branch_info->commit ?
 				       &new_branch_info->commit->object.oid :
@@ -846,82 +916,8 @@ static int merge_working_tree(const struct checkout_opts *opts,
 		ret = unpack_trees(2, trees, &topts);
 		clear_unpack_trees_porcelain(&topts);
 		if (ret == -1) {
-			/*
-			 * Unpack couldn't do a trivial merge; either
-			 * give up or do a real merge, depending on
-			 * whether the merge flag was used.
-			 */
-			struct tree *work;
-			struct tree *old_tree;
-			struct merge_options o;
-			struct strbuf sb = STRBUF_INIT;
-			struct strbuf old_commit_shortname = STRBUF_INIT;
-
-			if (!opts->merge)
-				return 1;
-
-			/*
-			 * Without old_branch_info->commit, the below is the same as
-			 * the two-tree unpack we already tried and failed.
-			 */
-			if (!old_branch_info->commit)
-				return 1;
-			old_tree = repo_get_commit_tree(the_repository,
-							old_branch_info->commit);
-
-			if (repo_index_has_changes(the_repository, old_tree, &sb))
-				die(_("cannot continue with staged changes in "
-				      "the following files:\n%s"), sb.buf);
-			strbuf_release(&sb);
-
-			/* Do more real merge */
-
-			/*
-			 * We update the index fully, then write the
-			 * tree from the index, then merge the new
-			 * branch with the current tree, with the old
-			 * branch as the base. Then we reset the index
-			 * (but not the working tree) to the new
-			 * branch, leaving the working tree as the
-			 * merged version, but skipping unmerged
-			 * entries in the index.
-			 */
-
-			add_files_to_cache(the_repository, NULL, NULL, NULL, 0,
-					0, 0);
-			init_ui_merge_options(&o, the_repository);
-			o.verbosity = 0;
-			work = write_in_core_index_as_tree(the_repository,
-							   the_repository->index);
-
-			ret = reset_tree(new_tree,
-					 opts, 1,
-					 writeout_error, new_branch_info);
-			if (ret)
-				return ret;
-			o.ancestor = old_branch_info->name;
-			if (!old_branch_info->name) {
-				strbuf_add_unique_abbrev(&old_commit_shortname,
-							 &old_branch_info->commit->object.oid,
-							 DEFAULT_ABBREV);
-				o.ancestor = old_commit_shortname.buf;
-			}
-			o.branch1 = new_branch_info->name;
-			o.branch2 = "local";
-			o.conflict_style = opts->conflict_style;
-			ret = merge_ort_nonrecursive(&o,
-						     new_tree,
-						     work,
-						     old_tree);
-			if (ret < 0)
-				die(NULL);
-			ret = reset_tree(new_tree,
-					 opts, 0,
-					 writeout_error, new_branch_info);
-			strbuf_release(&o.obuf);
-			strbuf_release(&old_commit_shortname);
-			if (ret)
-				return ret;
+			rollback_lock_file(&lock_file);
+			return MERGE_WORKING_TREE_UNPACK_FAILED;
 		}
 	}
 
@@ -1166,6 +1162,10 @@ static int switch_branches(const struct checkout_opts *opts,
 	struct object_id rev;
 	int flag, writeout_error = 0;
 	int do_merge = 1;
+	int created_autostash = 0;
+	struct strbuf old_commit_shortname = STRBUF_INIT;
+	struct strbuf autostash_msg = STRBUF_INIT;
+	const char *stash_label_base = NULL;
 
 	trace2_cmd_mode("branch");
 
@@ -1203,11 +1203,49 @@ static int switch_branches(const struct checkout_opts *opts,
 			do_merge = 0;
 	}
 
+	if (old_branch_info.name) {
+		stash_label_base = old_branch_info.name;
+	} else if (old_branch_info.commit) {
+		strbuf_add_unique_abbrev(&old_commit_shortname,
+					 &old_branch_info.commit->object.oid,
+					 DEFAULT_ABBREV);
+		stash_label_base = old_commit_shortname.buf;
+	}
+
 	if (do_merge) {
-		ret = merge_working_tree(opts, &old_branch_info, new_branch_info, &writeout_error);
+		ret = merge_working_tree(opts, &old_branch_info, new_branch_info,
+					 opts->merge, &writeout_error);
+		if (ret == MERGE_WORKING_TREE_UNPACK_FAILED && opts->merge) {
+			strbuf_addf(&autostash_msg,
+				    "autostash while switching to '%s'",
+				    new_branch_info->name);
+			create_autostash_ref(the_repository,
+					     "CHECKOUT_AUTOSTASH_HEAD",
+					     autostash_msg.buf, true);
+			created_autostash = 1;
+			ret = merge_working_tree(opts, &old_branch_info, new_branch_info,
+						 false, &writeout_error);
+		}
+		if (created_autostash) {
+			if (opts->conflict_style >= 0) {
+				struct strbuf cfg = STRBUF_INIT;
+				strbuf_addf(&cfg, "merge.conflictStyle=%s",
+					    conflict_style_name(opts->conflict_style));
+				git_config_push_parameter(cfg.buf);
+				strbuf_release(&cfg);
+			}
+			apply_autostash_ref(the_repository,
+					    "CHECKOUT_AUTOSTASH_HEAD",
+					    new_branch_info->name,
+					    "local",
+					    stash_label_base,
+					    autostash_msg.buf);
+		}
 		if (ret) {
 			branch_info_release(&old_branch_info);
-			return ret;
+			strbuf_release(&old_commit_shortname);
+			strbuf_release(&autostash_msg);
+			return ret < 0 ? 1 : ret;
 		}
 	}
 
@@ -1216,8 +1254,22 @@ static int switch_branches(const struct checkout_opts *opts,
 
 	update_refs_for_switch(opts, &old_branch_info, new_branch_info);
 
+	if (created_autostash) {
+		discard_index(the_repository->index);
+		if (repo_read_index(the_repository) < 0)
+			die(_("index file corrupt"));
+
+		if (!opts->quiet && new_branch_info->commit) {
+			printf(_("The following paths have local changes:\n"));
+			show_local_changes(&new_branch_info->commit->object,
+					   &opts->diff_options);
+		}
+	}
+
 	ret = post_checkout_hook(old_branch_info.commit, new_branch_info->commit, 1);
 	branch_info_release(&old_branch_info);
+	strbuf_release(&old_commit_shortname);
+	strbuf_release(&autostash_msg);
 
 	return ret || writeout_error;
 }
@@ -1485,7 +1537,7 @@ static int parse_branchname_arg(int argc, const char **argv,
 		 * it would be extremely annoying.
 		 */
 		if (argc)
-			verify_non_filename(opts->prefix, arg);
+			verify_non_filename(the_repository, opts->prefix, arg);
 	} else if (opts->accept_pathspec) {
 		argcount++;
 		argv++;
diff --git a/builtin/clone.c b/builtin/clone.c
index fba3c9c..d60d1b6 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -647,6 +647,7 @@ static int checkout(int submodule_progress,
 	struct tree *tree;
 	struct tree_desc t;
 	int err = 0;
+	struct run_hooks_opt hook_opt = RUN_HOOKS_OPT_INIT_FORCE_SERIAL;
 
 	if (option_no_checkout)
 		return 0;
@@ -668,7 +669,7 @@ static int checkout(int submodule_progress,
 	}
 
 	/* We need to be in the new work tree for the checkout */
-	setup_work_tree();
+	setup_work_tree(the_repository);
 
 	repo_hold_locked_index(the_repository, &lock_file, LOCK_DIE_ON_ERROR);
 
@@ -697,8 +698,9 @@ static int checkout(int submodule_progress,
 	if (write_locked_index(the_repository->index, &lock_file, COMMIT_LOCK))
 		die(_("unable to write new index file"));
 
-	err |= run_hooks_l(the_repository, "post-checkout", oid_to_hex(null_oid(the_hash_algo)),
-			   oid_to_hex(&oid), "1", NULL);
+	strvec_pushl(&hook_opt.args, oid_to_hex(null_oid(the_hash_algo)),
+		     oid_to_hex(&oid), "1", NULL);
+	err |= run_hooks_opt(the_repository, "post-checkout", &hook_opt);
 
 	if (!err && (option_recurse_submodules.nr > 0)) {
 		struct child_process cmd = CHILD_PROCESS_INIT;
@@ -1114,7 +1116,7 @@ int cmd_clone(int argc,
 			die_errno(_("could not create work tree dir '%s'"),
 				  work_tree);
 		junk_work_tree = work_tree;
-		set_git_work_tree(work_tree);
+		set_git_work_tree(the_repository, work_tree);
 	}
 
 	if (real_git_dir) {
@@ -1184,7 +1186,7 @@ int cmd_clone(int argc,
 	 * repository, and reference backends may persist that information into
 	 * their on-disk data structures.
 	 */
-	init_db(git_dir, real_git_dir, option_template, GIT_HASH_UNKNOWN,
+	init_db(the_repository, git_dir, real_git_dir, option_template, GIT_HASH_UNKNOWN,
 		ref_storage_format, NULL,
 		do_not_override_repo_unix_permissions, INIT_DB_QUIET | INIT_DB_SKIP_REFDB);
 
@@ -1227,7 +1229,7 @@ int cmd_clone(int argc,
 	 *
 	 * This is sufficient for Git commands to discover the Git directory.
 	 */
-	initialize_repository_version(GIT_HASH_UNKNOWN,
+	initialize_repository_version(the_repository, GIT_HASH_UNKNOWN,
 				      the_repository->ref_storage_format, 1);
 
 	refs_create_refdir_stubs(the_repository, git_dir, NULL);
@@ -1440,9 +1442,9 @@ int cmd_clone(int argc,
 	 * ours to the same thing.
 	 */
 	hash_algo = hash_algo_by_ptr(transport_get_hash_algo(transport));
-	initialize_repository_version(hash_algo, the_repository->ref_storage_format, 1);
+	initialize_repository_version(the_repository, hash_algo, the_repository->ref_storage_format, 1);
 	repo_set_hash_algo(the_repository, hash_algo);
-	create_reference_database(NULL, 1);
+	create_reference_database(the_repository, NULL, 1);
 
 	/*
 	 * Before fetching from the remote, download and install bundle
diff --git a/builtin/commit.c b/builtin/commit.c
index a3e52ac..28f6174 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -1979,7 +1979,8 @@ int cmd_commit(int argc,
 				     &oid, flags);
 	}
 
-	apply_autostash_ref(the_repository, "MERGE_AUTOSTASH");
+	apply_autostash_ref(the_repository, "MERGE_AUTOSTASH",
+			    NULL, NULL, NULL, NULL);
 
 cleanup:
 	free_commit_extra_headers(extra);
diff --git a/builtin/config.c b/builtin/config.c
index 7c4857b..cf4ba0f 100644
--- a/builtin/config.c
+++ b/builtin/config.c
@@ -838,6 +838,7 @@ static int get_urlmatch(const struct config_location_options *opts,
 			const char *var, const char *url)
 {
 	int ret;
+	char *section;
 	char *section_tail;
 	struct config_display_options display_opts = *_display_opts;
 	struct string_list_item *item;
@@ -851,8 +852,8 @@ static int get_urlmatch(const struct config_location_options *opts,
 	if (!url_normalize(url, &config.url))
 		die("%s", config.url.err);
 
-	config.section = xstrdup_tolower(var);
-	section_tail = strchr(config.section, '.');
+	config.section = section = xstrdup_tolower(var);
+	section_tail = strchr(section, '.');
 	if (section_tail) {
 		*section_tail = '\0';
 		config.key = section_tail + 1;
@@ -886,7 +887,7 @@ static int get_urlmatch(const struct config_location_options *opts,
 	string_list_clear(&values, 1);
 	free(config.url.url);
 
-	free((void *)config.section);
+	free(section);
 	return ret;
 }
 
diff --git a/builtin/describe.c b/builtin/describe.c
index bffeed1..1c47d7c 100644
--- a/builtin/describe.c
+++ b/builtin/describe.c
@@ -781,7 +781,7 @@ int cmd_describe(int argc,
 			struct rev_info revs;
 			int fd;
 
-			setup_work_tree();
+			setup_work_tree(the_repository);
 			prepare_repo_settings(the_repository);
 			the_repository->settings.command_requires_full_index = 0;
 			repo_read_index(the_repository);
diff --git a/builtin/diff-index.c b/builtin/diff-index.c
index 522dacf..3db7cff 100644
--- a/builtin/diff-index.c
+++ b/builtin/diff-index.c
@@ -69,7 +69,7 @@ int cmd_diff_index(int argc,
 	    rev.max_count != -1 || rev.min_age != -1 || rev.max_age != -1)
 		usage(diff_cache_usage);
 	if (!(option & DIFF_INDEX_CACHED)) {
-		setup_work_tree();
+		setup_work_tree(the_repository);
 		if (repo_read_index_preload(the_repository, &rev.diffopt.pathspec, 0) < 0) {
 			perror("repo_read_index_preload");
 			return -1;
diff --git a/builtin/diff.c b/builtin/diff.c
index 0b23c41..4b46e39 100644
--- a/builtin/diff.c
+++ b/builtin/diff.c
@@ -159,7 +159,7 @@ static void builtin_diff_index(struct rev_info *revs,
 	    revs->max_age != -1)
 		usage(builtin_diff_usage);
 	if (!(option & DIFF_INDEX_CACHED)) {
-		setup_work_tree();
+		setup_work_tree(the_repository);
 		if (repo_read_index_preload(the_repository,
 					    &revs->diffopt.pathspec, 0) < 0) {
 			die_errno("repo_read_index_preload");
@@ -281,7 +281,7 @@ static void builtin_diff_files(struct rev_info *revs, int argc, const char **arg
 	    (revs->diffopt.output_format & DIFF_FORMAT_PATCH))
 		diff_merges_set_dense_combined_if_unset(revs);
 
-	setup_work_tree();
+	setup_work_tree(the_repository);
 	if (repo_read_index_preload(the_repository, &revs->diffopt.pathspec,
 				    0) < 0) {
 		die_errno("repo_read_index_preload");
@@ -455,7 +455,7 @@ int cmd_diff(int argc,
 			break;
 	}
 
-	prefix = setup_git_directory_gently(&nongit);
+	prefix = setup_git_directory_gently(the_repository, &nongit);
 
 	if (!nongit) {
 		prepare_repo_settings(the_repository);
@@ -471,8 +471,8 @@ int cmd_diff(int argc,
 		 * as a colourful "diff" replacement.
 		 */
 		if (nongit || ((argc == i + 2) &&
-			       (!path_inside_repo(prefix, argv[i]) ||
-				!path_inside_repo(prefix, argv[i + 1]))))
+			       (!path_inside_repo(the_repository, prefix, argv[i]) ||
+				!path_inside_repo(the_repository, prefix, argv[i + 1]))))
 			no_index = DIFF_NO_INDEX_IMPLICIT;
 	}
 
diff --git a/builtin/difftool.c b/builtin/difftool.c
index e4bc1f8..2a21005 100644
--- a/builtin/difftool.c
+++ b/builtin/difftool.c
@@ -767,7 +767,7 @@ int cmd_difftool(int argc,
 		die(_("difftool requires worktree or --no-index"));
 
 	if (!no_index){
-		setup_work_tree();
+		setup_work_tree(repo);
 		setenv(GIT_DIR_ENVIRONMENT, absolute_path(repo_get_git_dir(repo)), 1);
 		setenv(GIT_WORK_TREE_ENVIRONMENT, absolute_path(repo_get_work_tree(repo)), 1);
 	} else if (dir_diff)
diff --git a/builtin/fast-export.c b/builtin/fast-export.c
index 13621b0..2eb43a2 100644
--- a/builtin/fast-export.c
+++ b/builtin/fast-export.c
@@ -64,7 +64,8 @@ static int parse_opt_sign_mode(const struct option *opt,
 	if (unset)
 		return 0;
 
-	if (parse_sign_mode(arg, val, NULL))
+	if (parse_sign_mode(arg, val, NULL) || (*val == SIGN_STRIP_IF_INVALID) ||
+	    (*val == SIGN_SIGN_IF_INVALID) || (*val == SIGN_ABORT_IF_INVALID))
 		return error(_("unknown %s mode: %s"), opt->long_name, arg);
 
 	return 0;
@@ -822,12 +823,6 @@ static void handle_commit(struct commit *commit, struct rev_info *rev,
 			die(_("encountered signed commit %s; use "
 			      "--signed-commits=<mode> to handle it"),
 			    oid_to_hex(&commit->object.oid));
-		case SIGN_STRIP_IF_INVALID:
-			die(_("'strip-if-invalid' is not a valid mode for "
-			      "git fast-export with --signed-commits=<mode>"));
-		case SIGN_SIGN_IF_INVALID:
-			die(_("'sign-if-invalid' is not a valid mode for "
-			      "git fast-export with --signed-commits=<mode>"));
 		default:
 			BUG("invalid signed_commit_mode value %d", signed_commit_mode);
 		}
@@ -970,12 +965,6 @@ static void handle_tag(const char *name, struct tag *tag)
 				die(_("encountered signed tag %s; use "
 				      "--signed-tags=<mode> to handle it"),
 				    oid_to_hex(&tag->object.oid));
-			case SIGN_STRIP_IF_INVALID:
-				die(_("'strip-if-invalid' is not a valid mode for "
-				      "git fast-export with --signed-tags=<mode>"));
-			case SIGN_SIGN_IF_INVALID:
-				die(_("'sign-if-invalid' is not a valid mode for "
-				      "git fast-export with --signed-tags=<mode>"));
 			default:
 				BUG("invalid signed_commit_mode value %d", signed_commit_mode);
 			}
diff --git a/builtin/fast-import.c b/builtin/fast-import.c
index 570fd04..82bc6dc 100644
--- a/builtin/fast-import.c
+++ b/builtin/fast-import.c
@@ -191,6 +191,7 @@ static const char *global_prefix;
 static enum sign_mode signed_tag_mode = SIGN_VERBATIM;
 static enum sign_mode signed_commit_mode = SIGN_VERBATIM;
 static const char *signed_commit_keyid;
+static const char *signed_tag_keyid;
 
 /* Memory pools */
 static struct mem_pool fi_mem_pool = {
@@ -2892,6 +2893,9 @@ static void handle_signature_if_invalid(struct strbuf *new_data,
 	ret = verify_commit_buffer(tmp_buf.buf, tmp_buf.len, &signature_check);
 
 	if (ret) {
+		if (mode == SIGN_ABORT_IF_INVALID)
+			die(_("aborting due to invalid signature"));
+
 		warn_invalid_signature(&signature_check, msg->buf, mode);
 
 		if (mode == SIGN_SIGN_IF_INVALID) {
@@ -2983,6 +2987,7 @@ static void parse_new_commit(const char *arg)
 		case SIGN_VERBATIM:
 		case SIGN_STRIP_IF_INVALID:
 		case SIGN_SIGN_IF_INVALID:
+		case SIGN_ABORT_IF_INVALID:
 			import_one_signature(&sig_sha1, &sig_sha256, v);
 			break;
 
@@ -3068,7 +3073,8 @@ static void parse_new_commit(const char *arg)
 			encoding);
 
 	if ((signed_commit_mode == SIGN_STRIP_IF_INVALID ||
-	     signed_commit_mode == SIGN_SIGN_IF_INVALID) &&
+	     signed_commit_mode == SIGN_SIGN_IF_INVALID ||
+	     signed_commit_mode == SIGN_ABORT_IF_INVALID) &&
 	    (sig_sha1.hash_algo || sig_sha256.hash_algo))
 		handle_signature_if_invalid(&new_data, &sig_sha1, &sig_sha256,
 					    &msg, signed_commit_mode);
@@ -3084,7 +3090,50 @@ static void parse_new_commit(const char *arg)
 	b->last_commit = object_count_by_type[OBJ_COMMIT];
 }
 
-static void handle_tag_signature(struct strbuf *msg, const char *name)
+static void handle_tag_signature_if_invalid(struct strbuf *buf,
+					    struct strbuf *msg,
+					    size_t sig_offset)
+{
+	struct strbuf signature = STRBUF_INIT;
+	struct strbuf payload = STRBUF_INIT;
+	struct signature_check sigc = { 0 };
+
+	strbuf_addbuf(&payload, buf);
+	strbuf_addch(&payload, '\n');
+	strbuf_add(&payload, msg->buf, sig_offset);
+	strbuf_add(&signature, msg->buf + sig_offset, msg->len - sig_offset);
+
+	sigc.payload_type = SIGNATURE_PAYLOAD_TAG;
+	sigc.payload = strbuf_detach(&payload, &sigc.payload_len);
+
+	if (!check_signature(&sigc, signature.buf, signature.len))
+		goto out;
+
+	if (signed_tag_mode == SIGN_ABORT_IF_INVALID)
+		die(_("aborting due to invalid signature"));
+
+	strbuf_setlen(msg, sig_offset);
+
+	if (signed_tag_mode == SIGN_SIGN_IF_INVALID) {
+		strbuf_attach(&payload, sigc.payload, sigc.payload_len,
+			      sigc.payload_len + 1);
+		sigc.payload = NULL;
+		strbuf_reset(&signature);
+
+		if (sign_buffer(&payload, &signature, signed_tag_keyid,
+				SIGN_BUFFER_USE_DEFAULT_KEY))
+			die(_("failed to sign tag object"));
+
+		strbuf_addbuf(msg, &signature);
+	}
+
+out:
+	signature_check_clear(&sigc);
+	strbuf_release(&signature);
+	strbuf_release(&payload);
+}
+
+static void handle_tag_signature(struct strbuf *buf, struct strbuf *msg, const char *name)
 {
 	size_t sig_offset = parse_signed_buffer(msg->buf, msg->len);
 
@@ -3110,17 +3159,16 @@ static void handle_tag_signature(struct strbuf *msg, const char *name)
 		/* Truncate the buffer to remove the signature */
 		strbuf_setlen(msg, sig_offset);
 		break;
+	case SIGN_ABORT_IF_INVALID:
+	case SIGN_SIGN_IF_INVALID:
+	case SIGN_STRIP_IF_INVALID:
+		handle_tag_signature_if_invalid(buf, msg, sig_offset);
+		break;
 
 	/* Third, aborting modes */
 	case SIGN_ABORT:
 		die(_("encountered signed tag; use "
 		      "--signed-tags=<mode> to handle it"));
-	case SIGN_STRIP_IF_INVALID:
-		die(_("'strip-if-invalid' is not a valid mode for "
-		      "git fast-import with --signed-tags=<mode>"));
-	case SIGN_SIGN_IF_INVALID:
-		die(_("'sign-if-invalid' is not a valid mode for "
-		      "git fast-import with --signed-tags=<mode>"));
 	default:
 		BUG("invalid signed_tag_mode value %d from tag '%s'",
 		    signed_tag_mode, name);
@@ -3190,8 +3238,6 @@ static void parse_new_tag(const char *arg)
 	/* tag payload/message */
 	parse_data(&msg, 0, NULL);
 
-	handle_tag_signature(&msg, t->name);
-
 	/* build the tag object */
 	strbuf_reset(&new_data);
 
@@ -3203,6 +3249,9 @@ static void parse_new_tag(const char *arg)
 	if (tagger)
 		strbuf_addf(&new_data,
 			    "tagger %s\n", tagger);
+
+	handle_tag_signature(&new_data, &msg, t->name);
+
 	strbuf_addch(&new_data, '\n');
 	strbuf_addbuf(&new_data, &msg);
 	free(tagger);
@@ -3713,7 +3762,7 @@ static int parse_one_option(const char *option)
 		if (parse_sign_mode(option, &signed_commit_mode, &signed_commit_keyid))
 			usagef(_("unknown --signed-commits mode '%s'"), option);
 	} else if (skip_prefix(option, "signed-tags=", &option)) {
-		if (parse_sign_mode(option, &signed_tag_mode, NULL))
+		if (parse_sign_mode(option, &signed_tag_mode, &signed_tag_keyid))
 			usagef(_("unknown --signed-tags mode '%s'"), option);
 	} else if (!strcmp(option, "quiet")) {
 		show_stats = 0;
diff --git a/builtin/fetch-pack.c b/builtin/fetch-pack.c
index d9e42ba..316badd 100644
--- a/builtin/fetch-pack.c
+++ b/builtin/fetch-pack.c
@@ -223,7 +223,7 @@ int cmd_fetch_pack(int argc,
 		int flags = args.verbose ? CONNECT_VERBOSE : 0;
 		if (args.diag_url)
 			flags |= CONNECT_DIAG_URL;
-		conn = git_connect(fd, dest, "git-upload-pack",
+		conn = git_connect(fd, dest, GIT_CONNECT_UPLOAD_PACK,
 				   args.uploadpack, flags);
 		if (!conn)
 			return args.diag_url ? 0 : 1;
diff --git a/builtin/fetch.c b/builtin/fetch.c
index 4795b2a..e4e8a72 100644
--- a/builtin/fetch.c
+++ b/builtin/fetch.c
@@ -98,7 +98,8 @@ static struct transport *gtransport;
 static struct transport *gsecondary;
 static struct refspec refmap = REFSPEC_INIT_FETCH;
 static struct string_list server_options = STRING_LIST_INIT_DUP;
-static struct string_list negotiation_tip = STRING_LIST_INIT_NODUP;
+static struct string_list negotiation_restrict = STRING_LIST_INIT_NODUP;
+static struct string_list negotiation_include = STRING_LIST_INIT_NODUP;
 
 struct fetch_config {
 	enum display_format display_format;
@@ -946,7 +947,7 @@ static int update_local_ref(struct ref *ref,
 	int fast_forward = 0;
 
 	if (!odb_has_object(the_repository->objects, &ref->new_oid,
-			    HAS_OBJECT_RECHECK_PACKED | HAS_OBJECT_FETCH_PROMISOR))
+			    ODB_HAS_OBJECT_RECHECK_PACKED | ODB_HAS_OBJECT_FETCH_PROMISOR))
 		die(_("object %s not found"), oid_to_hex(&ref->new_oid));
 
 	if (oideq(&ref->old_oid, &ref->new_oid)) {
@@ -1396,7 +1397,7 @@ static int check_exist_and_connected(struct ref *ref_map)
 	 */
 	for (r = rm; r; r = r->next) {
 		if (!odb_has_object(the_repository->objects, &r->old_oid,
-				    HAS_OBJECT_RECHECK_PACKED))
+				    ODB_HAS_OBJECT_RECHECK_PACKED))
 			return -1;
 	}
 
@@ -1534,23 +1535,29 @@ static int add_oid(const struct reference *ref, void *cb_data)
 	return 0;
 }
 
-static void add_negotiation_tips(struct git_transport_options *smart_options)
+static void add_negotiation_tips(struct string_list *input_list,
+				 struct oid_array **output_list,
+				 const char *argname)
 {
 	struct oid_array *oids = xcalloc(1, sizeof(*oids));
 	int i;
 
-	for (i = 0; i < negotiation_tip.nr; i++) {
-		const char *s = negotiation_tip.items[i].string;
+	for (i = 0; i < input_list->nr; i++) {
+		const char *s = input_list->items[i].string;
 		struct refs_for_each_ref_options opts = {
 			.pattern = s,
 		};
 		int old_nr;
 		if (!has_glob_specials(s)) {
 			struct object_id oid;
+
+			/* Ignore missing reference. */
 			if (repo_get_oid(the_repository, s, &oid))
-				die(_("%s is not a valid object"), s);
+				continue;
+			/* Fail on missing object pointed by ref. */
 			if (!odb_has_object(the_repository->objects, &oid, 0))
 				die(_("the object %s does not exist"), s);
+
 			oid_array_append(oids, &oid);
 			continue;
 		}
@@ -1558,10 +1565,10 @@ static void add_negotiation_tips(struct git_transport_options *smart_options)
 		refs_for_each_ref_ext(get_main_ref_store(the_repository),
 				      add_oid, oids, &opts);
 		if (old_nr == oids->nr)
-			warning("ignoring --negotiation-tip=%s because it does not match any refs",
-				s);
+			warning(_("ignoring %s=%s because it does not match any refs"),
+				argname, s);
 	}
-	smart_options->negotiation_tips = oids;
+	*output_list = oids;
 }
 
 static struct transport *prepare_transport(struct remote *remote, int deepen,
@@ -1595,11 +1602,50 @@ static struct transport *prepare_transport(struct remote *remote, int deepen,
 		set_option(transport, TRANS_OPT_LIST_OBJECTS_FILTER, spec);
 		set_option(transport, TRANS_OPT_FROM_PROMISOR, "1");
 	}
-	if (negotiation_tip.nr) {
+	if (negotiation_restrict.nr) {
 		if (transport->smart_options)
-			add_negotiation_tips(transport->smart_options);
+			add_negotiation_tips(&negotiation_restrict,
+					     &transport->smart_options->negotiation_restrict_tips,
+					     "--negotiation-restrict");
 		else
-			warning("ignoring --negotiation-tip because the protocol does not support it");
+			warning(_("ignoring %s because the protocol does not support it"),
+				"--negotiation-restrict");
+	} else if (remote->negotiation_restrict.nr) {
+		struct string_list_item *item;
+		for_each_string_list_item(item, &remote->negotiation_restrict)
+			string_list_append(&negotiation_restrict, item->string);
+		if (transport->smart_options)
+			add_negotiation_tips(&negotiation_restrict,
+					     &transport->smart_options->negotiation_restrict_tips,
+					     "--negotiation-restrict");
+		else {
+			struct strbuf config_name = STRBUF_INIT;
+			strbuf_addf(&config_name, "remote.%s.negotiationRestrict", remote->name);
+			warning(_("ignoring %s because the protocol does not support it"),
+				config_name.buf);
+			strbuf_release(&config_name);
+		}
+	}
+	if (negotiation_include.nr) {
+		if (transport->smart_options)
+			add_negotiation_tips(&negotiation_include,
+					     &transport->smart_options->negotiation_include_tips,
+					     "--negotiation-include");
+		else
+			warning(_("ignoring %s because the protocol does not support it"),
+				"--negotiation-include");
+	} else if (remote->negotiation_include.nr) {
+		if (transport->smart_options) {
+			add_negotiation_tips(&remote->negotiation_include,
+					     &transport->smart_options->negotiation_include_tips,
+					     "--negotiation-include");
+		} else {
+			struct strbuf config_name = STRBUF_INIT;
+			strbuf_addf(&config_name, "remote.%s.negotiationInclude", remote->name);
+			warning(_("ignoring %s because the protocol does not support it"),
+				config_name.buf);
+			strbuf_release(&config_name);
+		}
 	}
 	return transport;
 }
@@ -2565,8 +2611,11 @@ int cmd_fetch(int argc,
 			       N_("specify fetch refmap"), PARSE_OPT_NONEG, parse_refmap_arg),
 		OPT_STRING_LIST('o', "server-option", &server_options, N_("server-specific"), N_("option to transmit")),
 		OPT_IPVERSION(&family),
-		OPT_STRING_LIST(0, "negotiation-tip", &negotiation_tip, N_("revision"),
+		OPT_STRING_LIST(0, "negotiation-restrict", &negotiation_restrict, N_("revision"),
 				N_("report that we have only objects reachable from this object")),
+		OPT_ALIAS(0, "negotiation-tip", "negotiation-restrict"),
+		OPT_STRING_LIST(0, "negotiation-include", &negotiation_include, N_("revision"),
+				N_("ensure this ref is always sent as a negotiation have")),
 		OPT_BOOL(0, "negotiate-only", &negotiate_only,
 			 N_("do not fetch a packfile; instead, print ancestors of negotiation tips")),
 		OPT_PARSE_LIST_OBJECTS_FILTER(&filter_options),
@@ -2656,9 +2705,6 @@ int cmd_fetch(int argc,
 		config.display_format = DISPLAY_FORMAT_PORCELAIN;
 	}
 
-	if (negotiate_only && !negotiation_tip.nr)
-		die(_("--negotiate-only needs one or more --negotiation-tip=*"));
-
 	if (deepen_relative) {
 		if (deepen_relative < 0)
 			die(_("negative depth in --deepen is not supported"));
@@ -2746,14 +2792,19 @@ int cmd_fetch(int argc,
 		if (!remote)
 			die(_("must supply remote when using --negotiate-only"));
 		gtransport = prepare_transport(remote, 1, &filter_options);
-		if (gtransport->smart_options) {
-			gtransport->smart_options->acked_commits = &acked_commits;
-		} else {
+
+		if (!gtransport->smart_options) {
 			warning(_("protocol does not support --negotiate-only, exiting"));
 			result = 1;
 			trace2_region_leave("fetch", "negotiate-only", the_repository);
 			goto cleanup;
 		}
+		if (!gtransport->smart_options->negotiation_restrict_tips)
+			die(_("%s needs one or more %s"), "--negotiate-only",
+			    "--negotiation-restrict=*");
+
+		gtransport->smart_options->acked_commits = &acked_commits;
+
 		if (server_options.nr)
 			gtransport->server_options = &server_options;
 		result = transport_fetch_refs(gtransport, NULL);
diff --git a/builtin/fsck.c b/builtin/fsck.c
index 9bab32e..248f8ff 100644
--- a/builtin/fsck.c
+++ b/builtin/fsck.c
@@ -1,4 +1,3 @@
-#define USE_THE_REPOSITORY_VARIABLE
 #include "builtin.h"
 #include "gettext.h"
 #include "hex.h"
@@ -42,8 +41,8 @@ static int check_full = 1;
 static int connectivity_only;
 static int check_strict;
 static int keep_cache_objects;
-static struct fsck_options fsck_walk_options = FSCK_OPTIONS_DEFAULT;
-static struct fsck_options fsck_obj_options = FSCK_OPTIONS_DEFAULT;
+static struct fsck_options fsck_walk_options;
+static struct fsck_options fsck_obj_options;
 static int errors_found;
 static int write_lost_and_found;
 static int verbose;
@@ -66,14 +65,14 @@ static const char *describe_object(const struct object_id *oid)
 	return fsck_describe_object(&fsck_walk_options, oid);
 }
 
-static const char *printable_type(const struct object_id *oid,
+static const char *printable_type(struct repository *repo,
+				  const struct object_id *oid,
 				  enum object_type type)
 {
 	const char *ret;
 
 	if (type == OBJ_NONE)
-		type = odb_read_object_info(the_repository->objects,
-					    oid, NULL);
+		type = odb_read_object_info(repo->objects, oid, NULL);
 
 	ret = type_name(type);
 	if (!ret)
@@ -82,17 +81,17 @@ static const char *printable_type(const struct object_id *oid,
 	return ret;
 }
 
-static int objerror(struct object *obj, const char *err)
+static int objerror(struct repository *repo, struct object *obj, const char *err)
 {
 	errors_found |= ERROR_OBJECT;
 	/* TRANSLATORS: e.g. error in tree 01bfda: <more explanation> */
 	fprintf_ln(stderr, _("error in %s %s: %s"),
-		   printable_type(&obj->oid, obj->type),
+		   printable_type(repo, &obj->oid, obj->type),
 		   describe_object(&obj->oid), err);
 	return -1;
 }
 
-static int fsck_objects_error_func(struct fsck_options *o UNUSED,
+static int fsck_objects_error_func(struct fsck_options *o,
 				   void *fsck_report,
 				   enum fsck_msg_type msg_type,
 				   enum fsck_msg_id msg_id UNUSED,
@@ -106,13 +105,13 @@ static int fsck_objects_error_func(struct fsck_options *o UNUSED,
 	case FSCK_WARN:
 		/* TRANSLATORS: e.g. warning in tree 01bfda: <more explanation> */
 		fprintf_ln(stderr, _("warning in %s %s: %s"),
-			   printable_type(oid, object_type),
+			   printable_type(o->repo, oid, object_type),
 			   describe_object(oid), message);
 		return 0;
 	case FSCK_ERROR:
 		/* TRANSLATORS: e.g. error in tree 01bfda: <more explanation> */
 		fprintf_ln(stderr, _("error in %s %s: %s"),
-			   printable_type(oid, object_type),
+			   printable_type(o->repo, oid, object_type),
 			   describe_object(oid), message);
 		return 1;
 	default:
@@ -124,7 +123,7 @@ static int fsck_objects_error_func(struct fsck_options *o UNUSED,
 static struct object_array pending;
 
 static int mark_object(struct object *obj, enum object_type type,
-		       void *data, struct fsck_options *options UNUSED)
+		       void *data, struct fsck_options *options)
 {
 	struct object *parent = data;
 
@@ -136,7 +135,7 @@ static int mark_object(struct object *obj, enum object_type type,
 	if (!obj) {
 		/* ... these references to parent->fld are safe here */
 		printf_ln(_("broken link from %7s %s"),
-			  printable_type(&parent->oid, parent->type),
+			  printable_type(options->repo, &parent->oid, parent->type),
 			  describe_object(&parent->oid));
 		printf_ln(_("broken link from %7s %s"),
 			  (type == OBJ_ANY ? _("unknown") : type_name(type)),
@@ -147,13 +146,13 @@ static int mark_object(struct object *obj, enum object_type type,
 
 	if (type != OBJ_ANY && obj->type != type)
 		/* ... and the reference to parent is safe here */
-		objerror(parent, _("wrong object type in link"));
+		objerror(options->repo, parent, _("wrong object type in link"));
 
 	if (obj->flags & REACHABLE)
 		return 0;
 	obj->flags |= REACHABLE;
 
-	if (is_promisor_object(the_repository, &obj->oid))
+	if (is_promisor_object(options->repo, &obj->oid))
 		/*
 		 * Further recursion does not need to be performed on this
 		 * object since it is a promisor object (so it does not need to
@@ -162,13 +161,13 @@ static int mark_object(struct object *obj, enum object_type type,
 		return 0;
 
 	if (!(obj->flags & HAS_OBJ)) {
-		if (parent && !odb_has_object(the_repository->objects, &obj->oid,
-					      HAS_OBJECT_RECHECK_PACKED)) {
+		if (parent && !odb_has_object(options->repo->objects, &obj->oid,
+					      ODB_HAS_OBJECT_RECHECK_PACKED)) {
 			printf_ln(_("broken link from %7s %s\n"
 				    "              to %7s %s"),
-				  printable_type(&parent->oid, parent->type),
+				  printable_type(options->repo, &parent->oid, parent->type),
 				  describe_object(&parent->oid),
-				  printable_type(&obj->oid, obj->type),
+				  printable_type(options->repo, &obj->oid, obj->type),
 				  describe_object(&obj->oid));
 			errors_found |= ERROR_REACHABLE;
 		}
@@ -181,7 +180,7 @@ static int mark_object(struct object *obj, enum object_type type,
 
 static void mark_object_reachable(struct object *obj)
 {
-	mark_object(obj, OBJ_ANY, NULL, NULL);
+	mark_object(obj, OBJ_ANY, NULL, &fsck_walk_options);
 }
 
 static int traverse_one_object(struct object *obj)
@@ -195,13 +194,13 @@ static int traverse_one_object(struct object *obj)
 	return result;
 }
 
-static int traverse_reachable(void)
+static int traverse_reachable(struct repository *repo)
 {
 	struct progress *progress = NULL;
 	unsigned int nr = 0;
 	int result = 0;
 	if (show_progress)
-		progress = start_delayed_progress(the_repository,
+		progress = start_delayed_progress(repo,
 						  _("Checking connectivity"), 0);
 	while (pending.nr) {
 		result |= traverse_one_object(object_array_pop(&pending));
@@ -222,10 +221,11 @@ static int mark_used(struct object *obj, enum object_type type UNUSED,
 
 static int mark_unreachable_referents(const struct object_id *oid,
 				      struct object_info *oi UNUSED,
-				      void *data UNUSED)
+				      void *data)
 {
-	struct fsck_options options = FSCK_OPTIONS_DEFAULT;
-	struct object *obj = lookup_object(the_repository, oid);
+	struct repository *repo = data;
+	struct fsck_options options;
+	struct object *obj = lookup_object(data, oid);
 
 	if (!obj || !(obj->flags & HAS_OBJ))
 		return 0; /* not part of our original set */
@@ -237,12 +237,13 @@ static int mark_unreachable_referents(const struct object_id *oid,
 	 * (and we want to avoid parsing blobs).
 	 */
 	if (obj->type == OBJ_NONE) {
-		enum object_type type = odb_read_object_info(the_repository->objects,
+		enum object_type type = odb_read_object_info(repo->objects,
 							     &obj->oid, NULL);
 		if (type > 0)
 			object_as_type(obj, type, 0);
 	}
 
+	fsck_options_init(&options, repo, FSCK_OPTIONS_DEFAULT);
 	options.walk = mark_used;
 	fsck_walk(obj, NULL, &options);
 	if (obj->type == OBJ_TREE)
@@ -254,7 +255,7 @@ static int mark_unreachable_referents(const struct object_id *oid,
 /*
  * Check a single reachable object
  */
-static void check_reachable_object(struct object *obj)
+static void check_reachable_object(struct repository *repo, struct object *obj)
 {
 	/*
 	 * We obviously want the object to be parsed,
@@ -262,12 +263,12 @@ static void check_reachable_object(struct object *obj)
 	 * do a full fsck
 	 */
 	if (!(obj->flags & HAS_OBJ)) {
-		if (is_promisor_object(the_repository, &obj->oid))
+		if (is_promisor_object(repo, &obj->oid))
 			return;
-		if (has_object_pack(the_repository, &obj->oid))
+		if (has_object_pack(repo, &obj->oid))
 			return; /* it is in pack - forget about it */
 		printf_ln(_("missing %s %s"),
-			  printable_type(&obj->oid, obj->type),
+			  printable_type(repo, &obj->oid, obj->type),
 			  describe_object(&obj->oid));
 		errors_found |= ERROR_REACHABLE;
 		return;
@@ -277,7 +278,7 @@ static void check_reachable_object(struct object *obj)
 /*
  * Check a single unreachable object
  */
-static void check_unreachable_object(struct object *obj)
+static void check_unreachable_object(struct repository *repo, struct object *obj)
 {
 	/*
 	 * Missing unreachable object? Ignore it. It's not like
@@ -294,7 +295,7 @@ static void check_unreachable_object(struct object *obj)
 	 */
 	if (show_unreachable) {
 		printf_ln(_("unreachable %s %s"),
-			  printable_type(&obj->oid, obj->type),
+			  printable_type(repo, &obj->oid, obj->type),
 			  describe_object(&obj->oid));
 		return;
 	}
@@ -314,22 +315,22 @@ static void check_unreachable_object(struct object *obj)
 	if (!(obj->flags & USED)) {
 		if (show_dangling)
 			printf_ln(_("dangling %s %s"),
-				  printable_type(&obj->oid, obj->type),
+				  printable_type(repo, &obj->oid, obj->type),
 				  describe_object(&obj->oid));
 		if (write_lost_and_found) {
-			char *filename = repo_git_path(the_repository, "lost-found/%s/%s",
+			char *filename = repo_git_path(repo, "lost-found/%s/%s",
 				obj->type == OBJ_COMMIT ? "commit" : "other",
 				describe_object(&obj->oid));
 			FILE *f;
 
-			if (safe_create_leading_directories_const(the_repository, filename)) {
+			if (safe_create_leading_directories_const(repo, filename)) {
 				error(_("could not create lost-found"));
 				free(filename);
 				return;
 			}
 			f = xfopen(filename, "w");
 			if (obj->type == OBJ_BLOB) {
-				if (odb_stream_blob_to_fd(the_repository->objects, fileno(f),
+				if (odb_stream_blob_to_fd(repo->objects, fileno(f),
 							  &obj->oid, NULL, 1))
 					die_errno(_("could not write '%s'"), filename);
 			} else
@@ -349,23 +350,23 @@ static void check_unreachable_object(struct object *obj)
 	 */
 }
 
-static void check_object(struct object *obj)
+static void check_object(struct repository *repo, struct object *obj)
 {
 	if (verbose)
 		fprintf_ln(stderr, _("Checking %s"), describe_object(&obj->oid));
 
 	if (obj->flags & REACHABLE)
-		check_reachable_object(obj);
+		check_reachable_object(repo, obj);
 	else
-		check_unreachable_object(obj);
+		check_unreachable_object(repo, obj);
 }
 
-static void check_connectivity(void)
+static void check_connectivity(struct repository *repo)
 {
 	int i, max;
 
 	/* Traverse the pending reachable objects */
-	traverse_reachable();
+	traverse_reachable(repo);
 
 	/*
 	 * With --connectivity-only, we won't have actually opened and marked
@@ -383,24 +384,25 @@ static void check_connectivity(void)
 		 * and ignore any that weren't present in our earlier
 		 * traversal.
 		 */
-		odb_for_each_object(the_repository->objects, NULL,
-				    mark_unreachable_referents, NULL, 0);
+		odb_for_each_object(repo->objects, NULL,
+				    mark_unreachable_referents, repo, 0);
 	}
 
 	/* Look up all the requirements, warn about missing objects.. */
-	max = get_max_object_index(the_repository);
+	max = get_max_object_index(repo);
 	if (verbose)
 		fprintf_ln(stderr, _("Checking connectivity (%d objects)"), max);
 
 	for (i = 0; i < max; i++) {
-		struct object *obj = get_indexed_object(the_repository, i);
+		struct object *obj = get_indexed_object(repo, i);
 
 		if (obj)
-			check_object(obj);
+			check_object(repo, obj);
 	}
 }
 
-static int fsck_obj(struct object *obj, void *buffer, unsigned long size)
+static int fsck_obj(struct repository *repo,
+		    struct object *obj, void *buffer, unsigned long size)
 {
 	int err;
 
@@ -410,11 +412,11 @@ static int fsck_obj(struct object *obj, void *buffer, unsigned long size)
 
 	if (verbose)
 		fprintf_ln(stderr, _("Checking %s %s"),
-			   printable_type(&obj->oid, obj->type),
+			   printable_type(repo, &obj->oid, obj->type),
 			   describe_object(&obj->oid));
 
 	if (fsck_walk(obj, NULL, &fsck_obj_options))
-		objerror(obj, _("broken links"));
+		objerror(repo, obj, _("broken links"));
 	err = fsck_object(obj, buffer, size, &fsck_obj_options);
 	if (err)
 		goto out;
@@ -432,7 +434,7 @@ static int fsck_obj(struct object *obj, void *buffer, unsigned long size)
 
 		if (show_tags && tag->tagged) {
 			printf_ln(_("tagged %s %s (%s) in %s"),
-				  printable_type(&tag->tagged->oid, tag->tagged->type),
+				  printable_type(repo, &tag->tagged->oid, tag->tagged->type),
 				  describe_object(&tag->tagged->oid),
 				  tag->tag,
 				  describe_object(&tag->object.oid));
@@ -446,15 +448,16 @@ static int fsck_obj(struct object *obj, void *buffer, unsigned long size)
 }
 
 static int fsck_obj_buffer(const struct object_id *oid, enum object_type type,
-			   unsigned long size, void *buffer, int *eaten)
+			   unsigned long size, void *buffer, int *eaten, void *cb_data)
 {
+	struct repository *repo = cb_data;
+	struct object *obj;
+
 	/*
 	 * Note, buffer may be NULL if type is OBJ_BLOB. See
 	 * verify_packfile(), data_valid variable for details.
 	 */
-	struct object *obj;
-	obj = parse_object_buffer(the_repository, oid, type, size, buffer,
-				  eaten);
+	obj = parse_object_buffer(repo, oid, type, size, buffer, eaten);
 	if (!obj) {
 		errors_found |= ERROR_OBJECT;
 		return error(_("%s: object corrupt or missing"),
@@ -462,18 +465,19 @@ static int fsck_obj_buffer(const struct object_id *oid, enum object_type type,
 	}
 	obj->flags &= ~(REACHABLE | SEEN);
 	obj->flags |= HAS_OBJ;
-	return fsck_obj(obj, buffer, size);
+	return fsck_obj(repo, obj, buffer, size);
 }
 
 static int default_refs;
 
-static void fsck_handle_reflog_oid(const char *refname, struct object_id *oid,
-	timestamp_t timestamp)
+static void fsck_handle_reflog_oid(struct repository *repo,
+				   const char *refname, struct object_id *oid,
+				   timestamp_t timestamp)
 {
 	struct object *obj;
 
 	if (!is_null_oid(oid)) {
-		obj = lookup_object(the_repository, oid);
+		obj = lookup_object(repo, oid);
 		if (obj && (obj->flags & HAS_OBJ)) {
 			if (timestamp)
 				fsck_put_object_name(&fsck_walk_options, oid,
@@ -481,7 +485,7 @@ static void fsck_handle_reflog_oid(const char *refname, struct object_id *oid,
 						     refname, timestamp);
 			obj->flags |= USED;
 			mark_object_reachable(obj);
-		} else if (!is_promisor_object(the_repository, oid)) {
+		} else if (!is_promisor_object(repo, oid)) {
 			error(_("%s: invalid reflog entry %s"),
 			      refname, oid_to_hex(oid));
 			errors_found |= ERROR_REACHABLE;
@@ -493,8 +497,10 @@ static int fsck_handle_reflog_ent(const char *refname,
 				  struct object_id *ooid, struct object_id *noid,
 				  const char *email UNUSED,
 				  timestamp_t timestamp, int tz UNUSED,
-				  const char *message UNUSED, void *cb_data UNUSED)
+				  const char *message UNUSED, void *cb_data)
 {
+	struct repository *repo = cb_data;
+
 	if (now && timestamp > now)
 		return 0;
 
@@ -502,19 +508,20 @@ static int fsck_handle_reflog_ent(const char *refname,
 		fprintf_ln(stderr, _("Checking reflog %s->%s"),
 			   oid_to_hex(ooid), oid_to_hex(noid));
 
-	fsck_handle_reflog_oid(refname, ooid, 0);
-	fsck_handle_reflog_oid(refname, noid, timestamp);
+	fsck_handle_reflog_oid(repo, refname, ooid, 0);
+	fsck_handle_reflog_oid(repo, refname, noid, timestamp);
 	return 0;
 }
 
 static int fsck_handle_reflog(const char *logname, void *cb_data)
 {
 	struct strbuf refname = STRBUF_INIT;
+	struct worktree *wt = cb_data;
 
-	strbuf_worktree_ref(cb_data, &refname, logname);
-	refs_for_each_reflog_ent(get_main_ref_store(the_repository),
+	strbuf_worktree_ref(wt, &refname, logname);
+	refs_for_each_reflog_ent(get_main_ref_store(wt->repo),
 				 refname.buf, fsck_handle_reflog_ent,
-				 NULL);
+				 wt->repo);
 	strbuf_release(&refname);
 	return 0;
 }
@@ -532,14 +539,20 @@ struct snapshot {
 	/* TODO: Consider also snapshotting the index of each worktree. */
 };
 
+struct snapshot_ref_data {
+	struct repository *repo;
+	struct snapshot *snap;
+};
+
 static int snapshot_ref(const struct reference *ref, void *cb_data)
 {
-	struct snapshot *snap = cb_data;
+	struct snapshot_ref_data *data = cb_data;
+	struct snapshot *snap = data->snap;
 	struct object *obj;
 
-	obj = parse_object(the_repository, ref->oid);
+	obj = parse_object(data->repo, ref->oid);
 	if (!obj) {
-		if (is_promisor_object(the_repository, ref->oid)) {
+		if (is_promisor_object(data->repo, ref->oid)) {
 			/*
 			 * Increment default_refs anyway, because this is a
 			 * valid ref.
@@ -567,11 +580,12 @@ static int snapshot_ref(const struct reference *ref, void *cb_data)
 	return 0;
 }
 
-static int fsck_handle_ref(const struct reference *ref, void *cb_data UNUSED)
+static int fsck_handle_ref(const struct reference *ref, void *cb_data)
 {
+	struct repository *repo = cb_data;
 	struct object *obj;
 
-	obj = parse_object(the_repository, ref->oid);
+	obj = parse_object(repo, ref->oid);
 	obj->flags |= USED;
 	fsck_put_object_name(&fsck_walk_options,
 			     ref->oid, "%s", ref->name);
@@ -580,11 +594,16 @@ static int fsck_handle_ref(const struct reference *ref, void *cb_data UNUSED)
 	return 0;
 }
 
-static void snapshot_refs(struct snapshot *snap, int argc, const char **argv)
+static void snapshot_refs(struct repository *repo,
+			  struct snapshot *snap, int argc, const char **argv)
 {
 	struct refs_for_each_ref_options opts = {
 		.flags = REFS_FOR_EACH_INCLUDE_BROKEN,
 	};
+	struct snapshot_ref_data data = {
+		.repo = repo,
+		.snap = snap,
+	};
 	struct worktree **worktrees, **p;
 	const char *head_points_at;
 	struct object_id head_oid;
@@ -592,13 +611,13 @@ static void snapshot_refs(struct snapshot *snap, int argc, const char **argv)
 	for (int i = 0; i < argc; i++) {
 		const char *arg = argv[i];
 		struct object_id oid;
-		if (!repo_get_oid(the_repository, arg, &oid)) {
+		if (!repo_get_oid(repo, arg, &oid)) {
 			struct reference ref = {
 				.name = arg,
 				.oid = &oid,
 			};
 
-			snapshot_ref(&ref, snap);
+			snapshot_ref(&ref, &data);
 			continue;
 		}
 		error(_("invalid parameter: expected sha1, got '%s'"), arg);
@@ -610,8 +629,8 @@ static void snapshot_refs(struct snapshot *snap, int argc, const char **argv)
 		return;
 	}
 
-	refs_for_each_ref_ext(get_main_ref_store(the_repository),
-			      snapshot_ref, snap, &opts);
+	refs_for_each_ref_ext(get_main_ref_store(repo),
+			      snapshot_ref, &data, &opts);
 
 	worktrees = get_worktrees();
 	for (p = worktrees; *p; p++) {
@@ -620,7 +639,7 @@ static void snapshot_refs(struct snapshot *snap, int argc, const char **argv)
 
 		strbuf_worktree_ref(wt, &refname, "HEAD");
 
-		head_points_at = refs_resolve_ref_unsafe(get_main_ref_store(the_repository),
+		head_points_at = refs_resolve_ref_unsafe(get_main_ref_store(repo),
 							 refname.buf, 0, &head_oid, NULL);
 
 		if (head_points_at && !is_null_oid(&head_oid)) {
@@ -629,7 +648,7 @@ static void snapshot_refs(struct snapshot *snap, int argc, const char **argv)
 				.oid = &head_oid,
 			};
 
-			snapshot_ref(&ref, snap);
+			snapshot_ref(&ref, &data);
 		}
 		strbuf_release(&refname);
 
@@ -653,7 +672,7 @@ static void free_snapshot_refs(struct snapshot *snap)
 	free(snap->ref);
 }
 
-static void process_refs(struct snapshot *snap)
+static void process_refs(struct repository *repo, struct snapshot *snap)
 {
 	struct worktree **worktrees, **p;
 
@@ -662,7 +681,7 @@ static void process_refs(struct snapshot *snap)
 			.name = snap->ref[i].refname,
 			.oid = &snap->ref[i].oid,
 		};
-		fsck_handle_ref(&ref, NULL);
+		fsck_handle_ref(&ref, repo);
 	}
 
 	if (include_reflogs) {
@@ -694,27 +713,28 @@ static void process_refs(struct snapshot *snap)
 	}
 }
 
-struct for_each_loose_cb
-{
+struct for_each_loose_cb {
+	struct repository *repo;
 	struct progress *progress;
 };
 
 static int fsck_loose(const struct object_id *oid, const char *path,
-		      void *data UNUSED)
+		      void *cb_data)
 {
+	struct for_each_loose_cb *data = cb_data;
 	struct object *obj;
 	enum object_type type = OBJ_NONE;
 	unsigned long size;
 	void *contents = NULL;
 	int eaten;
 	struct object_info oi = OBJECT_INFO_INIT;
-	struct object_id real_oid = *null_oid(the_hash_algo);
+	struct object_id real_oid = *null_oid(data->repo->hash_algo);
 	int err = 0;
 
 	oi.sizep = &size;
 	oi.typep = &type;
 
-	if (read_loose_object(the_repository, path, oid, &real_oid, &contents, &oi) < 0) {
+	if (read_loose_object(data->repo, path, oid, &real_oid, &contents, &oi) < 0) {
 		if (contents && !oideq(&real_oid, oid))
 			err = error(_("%s: hash-path mismatch, found at: %s"),
 				    oid_to_hex(&real_oid), path);
@@ -731,7 +751,7 @@ static int fsck_loose(const struct object_id *oid, const char *path,
 	if (!contents && type != OBJ_BLOB)
 		BUG("read_loose_object streamed a non-blob");
 
-	obj = parse_object_buffer(the_repository, oid, type, size,
+	obj = parse_object_buffer(data->repo, oid, type, size,
 				  contents, &eaten);
 
 	if (!obj) {
@@ -745,7 +765,7 @@ static int fsck_loose(const struct object_id *oid, const char *path,
 
 	obj->flags &= ~(REACHABLE | SEEN);
 	obj->flags |= HAS_OBJ;
-	if (fsck_obj(obj, contents, size))
+	if (fsck_obj(data->repo, obj, contents, size))
 		errors_found |= ERROR_OBJECT;
 
 	if (!eaten)
@@ -769,10 +789,11 @@ static int fsck_subdir(unsigned int nr, const char *path UNUSED, void *data)
 	return 0;
 }
 
-static void fsck_source(struct odb_source *source)
+static void fsck_source(struct repository *repo, struct odb_source *source)
 {
 	struct progress *progress = NULL;
 	struct for_each_loose_cb cb_data = {
+		.repo = source->odb->repo,
 		.progress = progress,
 	};
 
@@ -780,7 +801,7 @@ static void fsck_source(struct odb_source *source)
 		fprintf_ln(stderr, _("Checking object directory"));
 
 	if (show_progress)
-		progress = start_progress(the_repository,
+		progress = start_progress(repo,
 					  _("Checking object directories"), 256);
 
 	for_each_loose_file_in_source(source, fsck_loose,
@@ -789,7 +810,7 @@ static void fsck_source(struct odb_source *source)
 	stop_progress(&progress);
 }
 
-static int fsck_cache_tree(struct cache_tree *it, const char *index_path)
+static int fsck_cache_tree(struct repository *repo, struct cache_tree *it, const char *index_path)
 {
 	int i;
 	int err = 0;
@@ -798,7 +819,7 @@ static int fsck_cache_tree(struct cache_tree *it, const char *index_path)
 		fprintf_ln(stderr, _("Checking cache tree of %s"), index_path);
 
 	if (0 <= it->entry_count) {
-		struct object *obj = parse_object(the_repository, &it->oid);
+		struct object *obj = parse_object(repo, &it->oid);
 		if (!obj) {
 			error(_("%s: invalid sha1 pointer in cache-tree of %s"),
 			      oid_to_hex(&it->oid), index_path);
@@ -809,10 +830,10 @@ static int fsck_cache_tree(struct cache_tree *it, const char *index_path)
 		fsck_put_object_name(&fsck_walk_options, &it->oid, ":");
 		mark_object_reachable(obj);
 		if (obj->type != OBJ_TREE)
-			err |= objerror(obj, _("non-tree in cache-tree"));
+			err |= objerror(repo, obj, _("non-tree in cache-tree"));
 	}
 	for (i = 0; i < it->subtree_nr; i++)
-		err |= fsck_cache_tree(it->down[i]->cache_tree, index_path);
+		err |= fsck_cache_tree(repo, it->down[i]->cache_tree, index_path);
 	return err;
 }
 
@@ -838,7 +859,7 @@ static int fsck_resolve_undo(struct index_state *istate,
 			if (!ru->mode[i] || !S_ISREG(ru->mode[i]))
 				continue;
 
-			obj = parse_object(the_repository, &ru->oid[i]);
+			obj = parse_object(istate->repo, &ru->oid[i]);
 			if (!obj) {
 				error(_("%s: invalid sha1 pointer in resolve-undo of %s"),
 				      oid_to_hex(&ru->oid[i]),
@@ -870,7 +891,7 @@ static void fsck_index(struct index_state *istate, const char *index_path,
 		mode = istate->cache[i]->ce_mode;
 		if (S_ISGITLINK(mode))
 			continue;
-		blob = lookup_blob(the_repository,
+		blob = lookup_blob(istate->repo,
 				   &istate->cache[i]->oid);
 		if (!blob)
 			continue;
@@ -883,15 +904,16 @@ static void fsck_index(struct index_state *istate, const char *index_path,
 		mark_object_reachable(obj);
 	}
 	if (istate->cache_tree)
-		fsck_cache_tree(istate->cache_tree, index_path);
+		fsck_cache_tree(istate->repo, istate->cache_tree, index_path);
 	fsck_resolve_undo(istate, index_path);
 }
 
 static int mark_object_for_connectivity(const struct object_id *oid,
 					struct object_info *oi UNUSED,
-					void *cb_data UNUSED)
+					void *cb_data)
 {
-	struct object *obj = lookup_unknown_object(the_repository, oid);
+	struct repository *repo = cb_data;
+	struct object *obj = lookup_unknown_object(repo, oid);
 	obj->flags |= HAS_OBJ;
 	return 0;
 }
@@ -906,7 +928,7 @@ static int check_pack_rev_indexes(struct repository *r, int show_progress)
 	if (show_progress) {
 		repo_for_each_pack(r, p)
 			pack_count++;
-		progress = start_delayed_progress(the_repository,
+		progress = start_delayed_progress(r,
 						  "Verifying reverse pack-indexes", pack_count);
 		pack_count = 0;
 	}
@@ -986,7 +1008,7 @@ static struct option fsck_opts[] = {
 int cmd_fsck(int argc,
 	     const char **argv,
 	     const char *prefix,
-	     struct repository *repo UNUSED)
+	     struct repository *repo)
 {
 	struct odb_source *source;
 	struct snapshot snap = {
@@ -1004,7 +1026,10 @@ int cmd_fsck(int argc,
 
 	argc = parse_options(argc, argv, prefix, fsck_opts, fsck_usage, 0);
 
+	fsck_options_init(&fsck_walk_options, repo, FSCK_OPTIONS_DEFAULT);
 	fsck_walk_options.walk = mark_object;
+
+	fsck_options_init(&fsck_obj_options, repo, FSCK_OPTIONS_DEFAULT);
 	fsck_obj_options.walk = mark_used;
 	fsck_obj_options.error_func = fsck_objects_error_func;
 	if (check_strict)
@@ -1023,11 +1048,11 @@ int cmd_fsck(int argc,
 	if (name_objects)
 		fsck_enable_object_names(&fsck_walk_options);
 
-	repo_config(the_repository, git_fsck_config, &fsck_obj_options);
-	prepare_repo_settings(the_repository);
+	repo_config(repo, git_fsck_config, &fsck_obj_options);
+	prepare_repo_settings(repo);
 
 	if (check_references)
-		fsck_refs(the_repository);
+		fsck_refs(repo);
 
 	/*
 	 * Take a snapshot of the refs before walking objects to avoid looking
@@ -1035,18 +1060,18 @@ int cmd_fsck(int argc,
 	 * objects. We can still walk over new objects that are added during the
 	 * execution of fsck but won't miss any objects that were reachable.
 	 */
-	snapshot_refs(&snap, argc, argv);
+	snapshot_refs(repo, &snap, argc, argv);
 
 	/* Ensure we get a "fresh" view of the odb */
-	odb_reprepare(the_repository->objects);
+	odb_reprepare(repo->objects);
 
 	if (connectivity_only) {
-		odb_for_each_object(the_repository->objects, NULL,
-				    mark_object_for_connectivity, NULL, 0);
+		odb_for_each_object(repo->objects, NULL,
+				    mark_object_for_connectivity, repo, 0);
 	} else {
-		odb_prepare_alternates(the_repository->objects);
-		for (source = the_repository->objects->sources; source; source = source->next)
-			fsck_source(source);
+		odb_prepare_alternates(repo->objects);
+		for (source = repo->objects->sources; source; source = source->next)
+			fsck_source(repo, source);
 
 		if (check_full) {
 			struct packed_git *p;
@@ -1054,20 +1079,20 @@ int cmd_fsck(int argc,
 			struct progress *progress = NULL;
 
 			if (show_progress) {
-				repo_for_each_pack(the_repository, p) {
+				repo_for_each_pack(repo, p) {
 					if (open_pack_index(p))
 						continue;
 					total += p->num_objects;
 				}
 
-				progress = start_progress(the_repository,
+				progress = start_progress(repo,
 							  _("Checking objects"), total);
 			}
 
-			repo_for_each_pack(the_repository, p) {
+			repo_for_each_pack(repo, p) {
 				/* verify gives error messages itself */
-				if (verify_pack(the_repository,
-						p, fsck_obj_buffer,
+				if (verify_pack(repo,
+						p, fsck_obj_buffer, repo,
 						progress, count))
 					errors_found |= ERROR_PACK;
 				count += p->num_objects;
@@ -1080,7 +1105,7 @@ int cmd_fsck(int argc,
 	}
 
 	/* Process the snapshotted refs and the reflogs. */
-	process_refs(&snap);
+	process_refs(repo, &snap);
 
 	/* If not given any explicit objects, process index files too. */
 	if (!argc)
@@ -1100,7 +1125,7 @@ int cmd_fsck(int argc,
 		for (p = worktrees; *p; p++) {
 			struct worktree *wt = *p;
 			struct index_state istate =
-				INDEX_STATE_INIT(the_repository);
+				INDEX_STATE_INIT(repo);
 			char *path, *wt_gitdir;
 
 			/*
@@ -1121,17 +1146,17 @@ int cmd_fsck(int argc,
 		free_worktrees(worktrees);
 	}
 
-	errors_found |= check_pack_rev_indexes(the_repository, show_progress);
-	if (verify_bitmap_files(the_repository))
+	errors_found |= check_pack_rev_indexes(repo, show_progress);
+	if (verify_bitmap_files(repo))
 		errors_found |= ERROR_BITMAP;
 
-	check_connectivity();
+	check_connectivity(repo);
 
-	if (the_repository->settings.core_commit_graph) {
+	if (repo->settings.core_commit_graph) {
 		struct child_process commit_graph_verify = CHILD_PROCESS_INIT;
 
-		odb_prepare_alternates(the_repository->objects);
-		for (source = the_repository->objects->sources; source; source = source->next) {
+		odb_prepare_alternates(repo->objects);
+		for (source = repo->objects->sources; source; source = source->next) {
 			child_process_init(&commit_graph_verify);
 			commit_graph_verify.git_cmd = 1;
 			strvec_pushl(&commit_graph_verify.args, "commit-graph",
@@ -1145,11 +1170,11 @@ int cmd_fsck(int argc,
 		}
 	}
 
-	if (the_repository->settings.core_multi_pack_index) {
+	if (repo->settings.core_multi_pack_index) {
 		struct child_process midx_verify = CHILD_PROCESS_INIT;
 
-		odb_prepare_alternates(the_repository->objects);
-		for (source = the_repository->objects->sources; source; source = source->next) {
+		odb_prepare_alternates(repo->objects);
+		for (source = repo->objects->sources; source; source = source->next) {
 			child_process_init(&midx_verify);
 			midx_verify.git_cmd = 1;
 			strvec_pushl(&midx_verify.args, "multi-pack-index",
diff --git a/builtin/fsmonitor--daemon.c b/builtin/fsmonitor--daemon.c
index 242c594..f920cf3 100644
--- a/builtin/fsmonitor--daemon.c
+++ b/builtin/fsmonitor--daemon.c
@@ -16,7 +16,7 @@
 #include "fsmonitor--daemon.h"
 
 #include "simple-ipc.h"
-#include "khash.h"
+#include "strmap.h"
 #include "run-command.h"
 #include "trace.h"
 #include "trace2.h"
@@ -86,6 +86,8 @@ static int do_as_client__send_stop(void)
 {
 	struct strbuf answer = STRBUF_INIT;
 	int ret;
+	int max_wait_ms = 30000;
+	int elapsed_ms = 0;
 
 	ret = fsmonitor_ipc__send_command("quit", &answer);
 
@@ -96,8 +98,16 @@ static int do_as_client__send_stop(void)
 		return ret;
 
 	trace2_region_enter("fsm_client", "polling-for-daemon-exit", NULL);
-	while (fsmonitor_ipc__get_state() == IPC_STATE__LISTENING)
+	while (fsmonitor_ipc__get_state() == IPC_STATE__LISTENING) {
+		if (elapsed_ms >= max_wait_ms) {
+			trace2_region_leave("fsm_client",
+					    "polling-for-daemon-exit", NULL);
+			return error(_("daemon did not stop within %d seconds"),
+				     max_wait_ms / 1000);
+		}
 		sleep_millisec(50);
+		elapsed_ms += 50;
+	}
 	trace2_region_leave("fsm_client", "polling-for-daemon-exit", NULL);
 
 	return 0;
@@ -197,20 +207,31 @@ static enum fsmonitor_cookie_item_result with_lock__wait_for_cookie(
 	unlink(cookie_pathname.buf);
 
 	/*
-	 * Technically, this is an infinite wait (well, unless another
-	 * thread sends us an abort).  I'd like to change this to
-	 * use `pthread_cond_timedwait()` and return an error/timeout
-	 * and let the caller do the trivial response thing, but we
-	 * don't have that routine in our thread-utils.
-	 *
-	 * After extensive beta testing I'm not really worried about
-	 * this.  Also note that the above open() and unlink() calls
-	 * will cause at least two FS events on that path, so the odds
-	 * of getting stuck are pretty slim.
+	 * Wait for the listener thread to observe the cookie file.
+	 * Time out after a short interval so that the client
+	 * does not hang forever if the filesystem does not deliver
+	 * events (e.g., on certain container/overlay filesystems
+	 * where inotify watches succeed but events never arrive).
 	 */
-	while (cookie->result == FCIR_INIT)
-		pthread_cond_wait(&state->cookies_cond,
-				  &state->main_lock);
+	{
+		struct timeval now;
+		struct timespec ts;
+		int err = 0;
+
+		gettimeofday(&now, NULL);
+		ts.tv_sec = now.tv_sec + 1;
+		ts.tv_nsec = now.tv_usec * 1000;
+
+		while (cookie->result == FCIR_INIT && !err)
+			err = pthread_cond_timedwait(&state->cookies_cond,
+						     &state->main_lock,
+						     &ts);
+		if (err == ETIMEDOUT && cookie->result == FCIR_INIT) {
+			trace_printf_key(&trace_fsmonitor,
+					 "cookie_wait timed out");
+			cookie->result = FCIR_ERROR;
+		}
+	}
 
 done:
 	hashmap_remove(&state->cookies, &cookie->entry, NULL);
@@ -653,8 +674,6 @@ static int fsmonitor_parse_client_token(const char *buf_token,
 	return 0;
 }
 
-KHASH_INIT(str, const char *, int, 0, kh_str_hash_func, kh_str_hash_equal)
-
 static int do_handle_client(struct fsmonitor_daemon_state *state,
 			    const char *command,
 			    ipc_server_reply_cb *reply,
@@ -671,8 +690,7 @@ static int do_handle_client(struct fsmonitor_daemon_state *state,
 	const struct fsmonitor_batch *batch;
 	struct fsmonitor_batch *remainder = NULL;
 	intmax_t count = 0, duplicates = 0;
-	kh_str_t *shown;
-	int hash_ret;
+	struct strset shown = STRSET_INIT;
 	int do_trivial = 0;
 	int do_flush = 0;
 	int do_cookie = 0;
@@ -861,14 +879,14 @@ static int do_handle_client(struct fsmonitor_daemon_state *state,
 	 * so walk the batch list backwards from the current head back
 	 * to the batch (sequence number) they named.
 	 *
-	 * We use khash to de-dup the list of pathnames.
+	 * We use a strset to de-dup the list of pathnames.
 	 *
 	 * NEEDSWORK: each batch contains a list of interned strings,
 	 * so we only need to do pointer comparisons here to build the
 	 * hash table.  Currently, we're still comparing the string
 	 * values.
 	 */
-	shown = kh_init_str();
+	strset_init_with_options(&shown, NULL, 0);
 	for (batch = batch_head;
 	     batch && batch->batch_seq_nr > requested_oldest_seq_nr;
 	     batch = batch->next) {
@@ -878,11 +896,9 @@ static int do_handle_client(struct fsmonitor_daemon_state *state,
 			const char *s = batch->interned_paths[k];
 			size_t s_len;
 
-			if (kh_get_str(shown, s) != kh_end(shown))
+			if (!strset_add(&shown, s))
 				duplicates++;
 			else {
-				kh_put_str(shown, s, &hash_ret);
-
 				trace_printf_key(&trace_fsmonitor,
 						 "send[%"PRIuMAX"]: %s",
 						 count, s);
@@ -909,8 +925,6 @@ static int do_handle_client(struct fsmonitor_daemon_state *state,
 		total_response_len += payload.len;
 	}
 
-	kh_release_str(shown);
-
 	pthread_mutex_lock(&state->main_lock);
 
 	if (token_data->client_ref_count > 0)
@@ -954,6 +968,7 @@ static int do_handle_client(struct fsmonitor_daemon_state *state,
 	trace2_data_intmax("fsmonitor", the_repository, "response/count/duplicates", duplicates);
 
 cleanup:
+	strset_clear(&shown);
 	strbuf_release(&response_token);
 	strbuf_release(&requested_token_id);
 	strbuf_release(&payload);
@@ -1405,6 +1420,15 @@ static int fsmonitor_run_daemon(void)
 done:
 	pthread_cond_destroy(&state.cookies_cond);
 	pthread_mutex_destroy(&state.main_lock);
+	{
+		struct hashmap_iter iter;
+		struct fsmonitor_cookie_item *cookie;
+
+		hashmap_for_each_entry(&state.cookies, &iter, cookie, entry)
+			free(cookie->name);
+		hashmap_clear_and_free(&state.cookies,
+				       struct fsmonitor_cookie_item, entry);
+	}
 	fsm_listen__dtor(&state);
 	fsm_health__dtor(&state);
 
@@ -1420,7 +1444,7 @@ static int fsmonitor_run_daemon(void)
 	return err;
 }
 
-static int try_to_run_foreground_daemon(int detach_console MAYBE_UNUSED)
+static int try_to_run_foreground_daemon(int detach_console)
 {
 	/*
 	 * Technically, we don't need to probe for an existing daemon
@@ -1440,10 +1464,21 @@ static int try_to_run_foreground_daemon(int detach_console MAYBE_UNUSED)
 		fflush(stderr);
 	}
 
+	if (detach_console) {
 #ifdef GIT_WINDOWS_NATIVE
-	if (detach_console)
 		FreeConsole();
+#else
+		/*
+		 * Create a new session so that the daemon is detached
+		 * from the parent's process group.  This prevents
+		 * shells with job control (e.g. bash with "set -m")
+		 * from waiting on the daemon when they wait for a
+		 * foreground command that implicitly spawned it.
+		 */
+		if (setsid() == -1)
+			warning_errno(_("setsid failed"));
 #endif
+	}
 
 	return !!fsmonitor_run_daemon();
 }
@@ -1506,6 +1541,7 @@ static int try_to_start_background_daemon(void)
 	cp.no_stdin = 1;
 	cp.no_stdout = 1;
 	cp.no_stderr = 1;
+	cp.close_fd_above_stderr = 1;
 
 	sbgr = start_bg_command(&cp, bg_wait_cb, NULL,
 				fsmonitor__start_timeout_sec);
diff --git a/builtin/gc.c b/builtin/gc.c
index 3a71e31..84a66d3 100644
--- a/builtin/gc.c
+++ b/builtin/gc.c
@@ -1590,6 +1590,7 @@ static int maintenance_task_geometric_repack(struct maintenance_run_opts *opts,
 	pack_geometry_split(&geometry);
 
 	child.git_cmd = 1;
+	child.odb_to_close = the_repository->objects;
 
 	strvec_pushl(&child.args, "repack", "-d", "-l", NULL);
 	if (geometry.split < geometry.pack_nr)
diff --git a/builtin/grep.c b/builtin/grep.c
index e33285e..6a09571 100644
--- a/builtin/grep.c
+++ b/builtin/grep.c
@@ -28,9 +28,12 @@
 #include "object-file.h"
 #include "object-name.h"
 #include "odb.h"
+#include "oid-array.h"
+#include "oidset.h"
 #include "packfile.h"
 #include "pager.h"
 #include "path.h"
+#include "promisor-remote.h"
 #include "read-cache-ll.h"
 #include "write-or-die.h"
 
@@ -692,6 +695,144 @@ static int grep_tree(struct grep_opt *opt, const struct pathspec *pathspec,
 	return hit;
 }
 
+static void collect_blob_oids_for_tree(struct repository *repo,
+				       const struct pathspec *pathspec,
+				       struct tree_desc *tree,
+				       struct strbuf *base,
+				       int tn_len,
+				       struct oidset *blob_oids)
+{
+	struct name_entry entry;
+	int old_baselen = base->len;
+	struct strbuf name = STRBUF_INIT;
+	enum interesting match = entry_not_interesting;
+
+	while (tree_entry(tree, &entry)) {
+		if (match != all_entries_interesting) {
+			strbuf_addstr(&name, base->buf + tn_len);
+			match = tree_entry_interesting(repo->index,
+						       &entry, &name,
+						       pathspec);
+			strbuf_reset(&name);
+
+			if (match == all_entries_not_interesting)
+				break;
+			if (match == entry_not_interesting)
+				continue;
+		}
+
+		strbuf_add(base, entry.path, tree_entry_len(&entry));
+
+		if (S_ISREG(entry.mode)) {
+			if (!odb_has_object(repo->objects, &entry.oid, 0))
+				oidset_insert(blob_oids, &entry.oid);
+		} else if (S_ISDIR(entry.mode)) {
+			enum object_type type;
+			struct tree_desc sub_tree;
+			void *data;
+			unsigned long size;
+
+			data = odb_read_object(repo->objects, &entry.oid,
+					       &type, &size);
+			if (!data)
+				die(_("unable to read tree (%s)"),
+				    oid_to_hex(&entry.oid));
+
+			strbuf_addch(base, '/');
+			init_tree_desc(&sub_tree, &entry.oid, data, size);
+			collect_blob_oids_for_tree(repo, pathspec, &sub_tree,
+						   base, tn_len, blob_oids);
+			free(data);
+		}
+		/*
+		 * ...no else clause for S_ISGITLINK: submodules have their
+		 * own promisor configuration and would need separate fetches
+		 * anyway.
+		 */
+
+		strbuf_setlen(base, old_baselen);
+	}
+
+	strbuf_release(&name);
+}
+
+static void collect_blob_oids_for_treeish(struct grep_opt *opt,
+					  const struct pathspec *pathspec,
+					  const struct object_id *tree_ish_oid,
+					  const char *name,
+					  struct oidset *blob_oids)
+{
+	struct tree_desc tree;
+	void *data;
+	unsigned long size;
+	struct strbuf base = STRBUF_INIT;
+	int len;
+
+	data = odb_read_object_peeled(opt->repo->objects, tree_ish_oid,
+				      OBJ_TREE, &size, NULL);
+
+	if (!data)
+		return;
+
+	len = name ? strlen(name) : 0;
+	if (len) {
+		strbuf_add(&base, name, len);
+		strbuf_addch(&base, ':');
+	}
+	init_tree_desc(&tree, tree_ish_oid, data, size);
+
+	collect_blob_oids_for_tree(opt->repo, pathspec, &tree,
+				   &base, base.len, blob_oids);
+
+	strbuf_release(&base);
+	free(data);
+}
+
+static void prefetch_grep_blobs(struct grep_opt *opt,
+				const struct pathspec *pathspec,
+				const struct object_array *list)
+{
+	struct oidset blob_oids = OIDSET_INIT;
+
+	/* Exit if we're not in a partial clone */
+	if (!repo_has_promisor_remote(opt->repo))
+		return;
+
+	/* For each tree, gather the blobs in it */
+	for (int i = 0; i < list->nr; i++) {
+		struct object *real_obj;
+
+		obj_read_lock();
+		real_obj = deref_tag(opt->repo, list->objects[i].item,
+				     NULL, 0);
+		obj_read_unlock();
+
+		if (real_obj &&
+		    (real_obj->type == OBJ_COMMIT ||
+		     real_obj->type == OBJ_TREE))
+			collect_blob_oids_for_treeish(opt, pathspec,
+						      &real_obj->oid,
+						      list->objects[i].name,
+						      &blob_oids);
+	}
+
+	/* Prefetch the blobs we found */
+	if (oidset_size(&blob_oids)) {
+		struct oid_array to_fetch = OID_ARRAY_INIT;
+		struct oidset_iter iter;
+		const struct object_id *oid;
+
+		oidset_iter_init(&blob_oids, &iter);
+		while ((oid = oidset_iter_next(&iter)))
+			oid_array_append(&to_fetch, oid);
+
+		promisor_remote_get_direct(opt->repo, to_fetch.oid, to_fetch.nr);
+
+		oid_array_clear(&to_fetch);
+	}
+	oidset_clear(&blob_oids);
+}
+
 static int grep_object(struct grep_opt *opt, const struct pathspec *pathspec,
 		       struct object *obj, const char *name, const char *path)
 {
@@ -732,6 +873,8 @@ static int grep_objects(struct grep_opt *opt, const struct pathspec *pathspec,
 	int hit = 0;
 	const unsigned int nr = list->nr;
 
+	prefetch_grep_blobs(opt, pathspec, list);
+
 	for (i = 0; i < nr; i++) {
 		struct object *real_obj;
 
@@ -1064,7 +1207,7 @@ int cmd_grep(int argc,
 			use_index = 0;
 		else
 			/* die the same way as if we did it at the beginning */
-			setup_git_directory();
+			setup_git_directory(the_repository);
 	}
 	/* Ignore --recurse-submodules if --no-index is given or implied */
 	if (!use_index)
@@ -1151,7 +1294,7 @@ int cmd_grep(int argc,
 
 		object = parse_object_or_die(the_repository, &oid, arg);
 		if (!seen_dashdash)
-			verify_non_filename(prefix, arg);
+			verify_non_filename(the_repository, prefix, arg);
 		add_object_array_with_path(object, arg, &list, oc.mode, oc.path);
 		object_context_release(&oc);
 	}
@@ -1163,7 +1306,7 @@ int cmd_grep(int argc,
 	if (!seen_dashdash) {
 		int j;
 		for (j = i; j < argc; j++)
-			verify_filename(prefix, argv[j], j == i && allow_revs);
+			verify_filename(the_repository, prefix, argv[j], j == i && allow_revs);
 	}
 
 	parse_pathspec(&pathspec, 0,
@@ -1272,7 +1415,7 @@ int cmd_grep(int argc,
 		die(_("--[no-]exclude-standard cannot be used for tracked contents"));
 	} else if (!list.nr) {
 		if (!cached)
-			setup_work_tree();
+			setup_work_tree(the_repository);
 
 		hit = grep_cache(&opt, &pathspec, cached);
 	} else {
diff --git a/builtin/hash-object.c b/builtin/hash-object.c
index 5d900a6..f306b06 100644
--- a/builtin/hash-object.c
+++ b/builtin/hash-object.c
@@ -100,9 +100,9 @@ int cmd_hash_object(int argc,
 			     hash_object_usage, 0);
 
 	if (flags & INDEX_WRITE_OBJECT)
-		prefix = setup_git_directory();
+		prefix = setup_git_directory(the_repository);
 	else
-		prefix = setup_git_directory_gently(&nongit);
+		prefix = setup_git_directory_gently(the_repository, &nongit);
 
 	if (nongit && !the_hash_algo)
 		repo_set_hash_algo(the_repository, GIT_HASH_DEFAULT);
diff --git a/builtin/help.c b/builtin/help.c
index c0aece4..a140339 100644
--- a/builtin/help.c
+++ b/builtin/help.c
@@ -740,7 +740,7 @@ int cmd_help(int argc,
 		return 0;
 	}
 
-	setup_git_directory_gently(&nongit);
+	setup_git_directory_gently(the_repository, &nongit);
 	repo_config(the_repository, git_help_config, NULL);
 
 	if (parsed_help_format != HELP_FORMAT_NONE)
diff --git a/builtin/history.c b/builtin/history.c
index 568dc75..091465a 100644
--- a/builtin/history.c
+++ b/builtin/history.c
@@ -10,6 +10,7 @@
 #include "gettext.h"
 #include "hex.h"
 #include "lockfile.h"
+#include "merge-ort.h"
 #include "oidmap.h"
 #include "parse-options.h"
 #include "path.h"
@@ -23,6 +24,8 @@
 #include "unpack-trees.h"
 #include "wt-status.h"
 
+#define GIT_HISTORY_FIXUP_USAGE \
+	N_("git history fixup <commit> [--dry-run] [--update-refs=(branches|head)] [--reedit-message] [--empty=(drop|keep|abort)]")
 #define GIT_HISTORY_REWORD_USAGE \
 	N_("git history reword <commit> [--dry-run] [--update-refs=(branches|head)]")
 #define GIT_HISTORY_SPLIT_USAGE \
@@ -91,13 +94,18 @@ static int fill_commit_message(struct repository *repo,
 	return 0;
 }
 
-static int commit_tree_with_edited_message_ext(struct repository *repo,
-					       const char *action,
-					       struct commit *commit_with_message,
-					       const struct commit_list *parents,
-					       const struct object_id *old_tree,
-					       const struct object_id *new_tree,
-					       struct commit **out)
+enum commit_tree_flags {
+	COMMIT_TREE_EDIT_MESSAGE = (1 << 0),
+};
+
+static int commit_tree_ext(struct repository *repo,
+			   const char *action,
+			   struct commit *commit_with_message,
+			   const struct commit_list *parents,
+			   const struct object_id *old_tree,
+			   const struct object_id *new_tree,
+			   struct commit **out,
+			   enum commit_tree_flags flags)
 {
 	const char *exclude_gpgsig[] = {
 		/* We reencode the message, so the encoding needs to be stripped. */
@@ -122,10 +130,14 @@ static int commit_tree_with_edited_message_ext(struct repository *repo,
 		original_author = xmemdupz(ptr, len);
 	find_commit_subject(original_message, &original_body);
 
-	ret = fill_commit_message(repo, old_tree, new_tree,
-				  original_body, action, &commit_message);
-	if (ret < 0)
-		goto out;
+	if (flags & COMMIT_TREE_EDIT_MESSAGE) {
+		ret = fill_commit_message(repo, old_tree, new_tree,
+					  original_body, action, &commit_message);
+		if (ret < 0)
+			goto out;
+	} else {
+		strbuf_addstr(&commit_message, original_body);
+	}
 
 	original_extra_headers = read_commit_extra_headers(commit_with_message,
 							   exclude_gpgsig);
@@ -168,8 +180,8 @@ static int commit_tree_with_edited_message(struct repository *repo,
 		oidcpy(&parent_tree_oid, repo->hash_algo->empty_tree);
 	}
 
-	return commit_tree_with_edited_message_ext(repo, action, original, original->parents,
-						   &parent_tree_oid, tree_oid, out);
+	return commit_tree_ext(repo, action, original, original->parents,
+			       &parent_tree_oid, tree_oid, out, COMMIT_TREE_EDIT_MESSAGE);
 }
 
 enum ref_action {
@@ -272,7 +284,7 @@ static int setup_revwalk(struct repository *repo,
 
 		commit_list_insert(original, &from_list);
 		ret = repo_is_descendant_of(repo, head, from_list);
-		free_commit_list(from_list);
+		commit_list_free(from_list);
 
 		if (ret < 0) {
 			ret = error(_("cannot determine descendance"));
@@ -326,10 +338,13 @@ static int handle_reference_updates(struct rev_info *revs,
 				    struct commit *original,
 				    struct commit *rewritten,
 				    const char *reflog_msg,
-				    int dry_run)
+				    int dry_run,
+				    enum replay_empty_commit_action empty)
 {
 	const struct name_decoration *decoration;
-	struct replay_revisions_options opts = { 0 };
+	struct replay_revisions_options opts = {
+		.empty = empty,
+	};
 	struct replay_result result = { 0 };
 	struct ref_transaction *transaction = NULL;
 	struct strbuf err = STRBUF_INIT;
@@ -425,6 +440,236 @@ static int handle_reference_updates(struct rev_info *revs,
 	return ret;
 }
 
+static int commit_became_empty(struct repository *repo,
+			       struct commit *original,
+			       struct tree *result)
+{
+	struct commit *parent = original->parents ? original->parents->item : NULL;
+	struct object_id parent_tree_oid;
+
+	if (parent) {
+		if (repo_parse_commit(repo, parent))
+			return error(_("unable to parse parent of %s"),
+				     oid_to_hex(&original->object.oid));
+
+		parent_tree_oid = repo_get_commit_tree(repo, parent)->object.oid;
+	} else {
+		oidcpy(&parent_tree_oid, repo->hash_algo->empty_tree);
+	}
+
+	return oideq(&result->object.oid, &parent_tree_oid);
+}
+
+static int parse_opt_empty(const struct option *opt, const char *arg, int unset)
+{
+	enum replay_empty_commit_action *value = opt->value;
+
+	BUG_ON_OPT_NEG(unset);
+
+	if (!strcmp(arg, "drop"))
+		*value = REPLAY_EMPTY_COMMIT_DROP;
+	else if (!strcmp(arg, "keep"))
+		*value = REPLAY_EMPTY_COMMIT_KEEP;
+	else if (!strcmp(arg, "abort"))
+		*value = REPLAY_EMPTY_COMMIT_ABORT;
+	else
+		die(_("unrecognized '--empty=' action '%s'; "
+		      "valid values are \"drop\", \"keep\", and \"abort\"."), arg);
+
+	return 0;
+}
+
+static int cmd_history_fixup(int argc,
+			     const char **argv,
+			     const char *prefix,
+			     struct repository *repo)
+{
+	const char * const usage[] = {
+		GIT_HISTORY_FIXUP_USAGE,
+		NULL,
+	};
+	enum replay_empty_commit_action empty = REPLAY_EMPTY_COMMIT_DROP;
+	enum ref_action action = REF_ACTION_DEFAULT;
+	enum commit_tree_flags flags = 0;
+	int dry_run = 0;
+	struct option options[] = {
+		OPT_CALLBACK_F(0, "update-refs", &action, "(branches|head)",
+			       N_("control which refs should be updated"),
+			       PARSE_OPT_NONEG, parse_ref_action),
+		OPT_BOOL('n', "dry-run", &dry_run,
+			 N_("perform a dry-run without updating any refs")),
+		OPT_BIT(0, "reedit-message", &flags,
+			N_("open an editor to modify the commit message"),
+			COMMIT_TREE_EDIT_MESSAGE),
+		OPT_CALLBACK_F(0, "empty", &empty, "(drop|keep|abort)",
+			       N_("how to handle commits that become empty"),
+			       PARSE_OPT_NONEG, parse_opt_empty),
+		OPT_END(),
+	};
+	struct merge_result merge_result = { 0 };
+	struct merge_options merge_opts = { 0 };
+	struct strbuf reflog_msg = STRBUF_INIT;
+	struct commit *head_commit, *original, *rewritten;
+	struct tree *head_tree, *original_tree, *index_tree;
+	struct rev_info revs = { 0 };
+	bool skip_commit = false;
+	int ret;
+
+	argc = parse_options(argc, argv, prefix, options, usage, 0);
+	if (argc != 1) {
+		ret = error(_("command expects a single revision"));
+		goto out;
+	}
+	repo_config(repo, git_default_config, NULL);
+
+	if (action == REF_ACTION_DEFAULT)
+		action = REF_ACTION_BRANCHES;
+
+	if (is_bare_repository()) {
+		ret = error(_("cannot run fixup in a bare repository"));
+		goto out;
+	}
+
+	/* Resolve the original commit, which is the one we want to fix up. */
+	original = lookup_commit_reference_by_name(argv[0]);
+	if (!original) {
+		ret = error(_("commit cannot be found: %s"), argv[0]);
+		goto out;
+	}
+
+	/*
+	 * Resolve HEAD so we can use its tree as the merge base: the staged
+	 * changes are expressed as a diff from HEAD's tree to the index tree.
+	 */
+	head_commit = lookup_commit_reference_by_name("HEAD");
+	if (!head_commit) {
+		ret = error(_("cannot look up HEAD"));
+		goto out;
+	}
+
+	head_tree = repo_get_commit_tree(repo, head_commit);
+	if (!head_tree) {
+		ret = error(_("cannot get tree for HEAD"));
+		goto out;
+	}
+
+	if (repo_read_index(repo) < 0) {
+		ret = error(_("unable to read index"));
+		goto out;
+	}
+
+	if (!repo_index_has_changes(repo, head_tree, NULL)) {
+		ret = error(_("nothing to fixup: no staged changes"));
+		goto out;
+	}
+
+	/*
+	 * Write the index as a tree object. This is the "theirs" side of the
+	 * three-way merge: it is HEAD's tree with the staged changes applied.
+	 */
+	index_tree = write_in_core_index_as_tree(repo, repo->index);
+	if (!index_tree) {
+		ret = error(_("unable to write index as a tree"));
+		goto out;
+	}
+
+	original_tree = repo_get_commit_tree(repo, original);
+	if (!original_tree) {
+		ret = error(_("cannot get tree for commit %s"), argv[0]);
+		goto out;
+	}
+
+	/*
+	 * Perform the three-way merge to reapply changes in the index onto the
+	 * target commit. This is using basically the same logic as a
+	 * cherry-pick, where the base commit is our HEAD, ours is the original
+	 * tree and theirs is the index tree.
+	 */
+	init_basic_merge_options(&merge_opts, repo);
+	merge_opts.ancestor = "HEAD";
+	merge_opts.branch1 = argv[0];
+	merge_opts.branch2 = "staged";
+	merge_incore_nonrecursive(&merge_opts, head_tree,
+				  original_tree, index_tree, &merge_result);
+
+	if (merge_result.clean < 0) {
+		ret = error(_("merge failed while applying fixup"));
+		goto out;
+	}
+
+	if (!merge_result.clean) {
+		ret = error(_("fixup would produce conflicts; aborting"));
+		goto out;
+	}
+
+	ret = commit_became_empty(repo, original, merge_result.tree);
+	if (ret < 0)
+		goto out;
+	if (ret > 0) {
+		switch (empty) {
+		case REPLAY_EMPTY_COMMIT_DROP:
+			/*
+			 * Drop the target commit by replaying its descendants
+			 * directly onto its parent.
+			 */
+			rewritten = original->parents ? original->parents->item : NULL;
+
+			/*
+			 * TODO: we don't yet have the ability to drop root
+			 * commits, but there's ultimately no good reason for
+			 * this restriction to exist other than a technical
+			 * limitation.
+			 */
+			if (!rewritten) {
+				ret = error(_("cannot drop root commit %s: "
+					      "it has no parent to replay onto"),
+					    argv[0]);
+				goto out;
+			}
+
+			skip_commit = true;
+			break;
+		case REPLAY_EMPTY_COMMIT_KEEP:
+			/* Proceed and record the empty commit. */
+			break;
+		case REPLAY_EMPTY_COMMIT_ABORT:
+			ret = error(_("fixup makes commit %s empty"), argv[0]);
+			goto out;
+		}
+	}
+
+	ret = setup_revwalk(repo, action, original, &revs);
+	if (ret)
+		goto out;
+
+	if (!skip_commit) {
+		ret = commit_tree_ext(repo, "fixup", original, original->parents,
+				      &original_tree->object.oid, &merge_result.tree->object.oid,
+				      &rewritten, flags);
+		if (ret < 0) {
+			ret = error(_("failed writing fixed-up commit"));
+			goto out;
+		}
+	}
+
+	strbuf_addf(&reflog_msg, "fixup: updating %s", argv[0]);
+
+	ret = handle_reference_updates(&revs, action, original, rewritten,
+				       reflog_msg.buf, dry_run, empty);
+	if (ret < 0) {
+		ret = error(_("failed replaying descendants"));
+		goto out;
+	}
+
+	ret = 0;
+
+out:
+	merge_finalize(&merge_opts, &merge_result);
+	strbuf_release(&reflog_msg);
+	release_revisions(&revs);
+	return ret;
+}
+
 static int cmd_history_reword(int argc,
 			      const char **argv,
 			      const char *prefix,
@@ -437,8 +682,8 @@ static int cmd_history_reword(int argc,
 	enum ref_action action = REF_ACTION_DEFAULT;
 	int dry_run = 0;
 	struct option options[] = {
-		OPT_CALLBACK_F(0, "update-refs", &action, N_("<action>"),
-			       N_("control which refs should be updated (branches|head)"),
+		OPT_CALLBACK_F(0, "update-refs", &action, "(branches|head)",
+			       N_("control which refs should be updated"),
 			       PARSE_OPT_NONEG, parse_ref_action),
 		OPT_BOOL('n', "dry-run", &dry_run,
 			 N_("perform a dry-run without updating any refs")),
@@ -478,7 +723,7 @@ static int cmd_history_reword(int argc,
 	strbuf_addf(&reflog_msg, "reword: updating %s", argv[0]);
 
 	ret = handle_reference_updates(&revs, action, original, rewritten,
-				       reflog_msg.buf, dry_run);
+				       reflog_msg.buf, dry_run, REPLAY_EMPTY_COMMIT_ABORT);
 	if (ret < 0) {
 		ret = error(_("failed replaying descendants"));
 		goto out;
@@ -616,9 +861,8 @@ static int split_commit(struct repository *repo,
 	 * The first commit is constructed from the split-out tree. The base
 	 * that shall be diffed against is the parent of the original commit.
 	 */
-	ret = commit_tree_with_edited_message_ext(repo, "split-out", original,
-						  original->parents, &parent_tree_oid,
-						  &split_tree->object.oid, &first_commit);
+	ret = commit_tree_ext(repo, "split-out", original, original->parents, &parent_tree_oid,
+			      &split_tree->object.oid, &first_commit, COMMIT_TREE_EDIT_MESSAGE);
 	if (ret < 0) {
 		ret = error(_("failed writing first commit"));
 		goto out;
@@ -634,9 +878,8 @@ static int split_commit(struct repository *repo,
 	old_tree_oid = &repo_get_commit_tree(repo, first_commit)->object.oid;
 	new_tree_oid = &repo_get_commit_tree(repo, original)->object.oid;
 
-	ret = commit_tree_with_edited_message_ext(repo, "split-out", original,
-						  parents, old_tree_oid,
-						  new_tree_oid, &second_commit);
+	ret = commit_tree_ext(repo, "split-out", original, parents, old_tree_oid,
+			      new_tree_oid, &second_commit, COMMIT_TREE_EDIT_MESSAGE);
 	if (ret < 0) {
 		ret = error(_("failed writing second commit"));
 		goto out;
@@ -649,7 +892,7 @@ static int split_commit(struct repository *repo,
 	if (index_file.len)
 		unlink(index_file.buf);
 	strbuf_release(&index_file);
-	free_commit_list(parents);
+	commit_list_free(parents);
 	release_index(&index);
 	return ret;
 }
@@ -666,8 +909,8 @@ static int cmd_history_split(int argc,
 	enum ref_action action = REF_ACTION_DEFAULT;
 	int dry_run = 0;
 	struct option options[] = {
-		OPT_CALLBACK_F(0, "update-refs", &action, N_("<refs>"),
-			       N_("control ref update behavior (branches|head|print)"),
+		OPT_CALLBACK_F(0, "update-refs", &action, "(branches|head)",
+			       N_("control ref update behavior"),
 			       PARSE_OPT_NONEG, parse_ref_action),
 		OPT_BOOL('n', "dry-run", &dry_run,
 			 N_("perform a dry-run without updating any refs")),
@@ -717,7 +960,7 @@ static int cmd_history_split(int argc,
 	strbuf_addf(&reflog_msg, "split: updating %s", argv[0]);
 
 	ret = handle_reference_updates(&revs, action, original, rewritten,
-				       reflog_msg.buf, dry_run);
+				       reflog_msg.buf, dry_run, REPLAY_EMPTY_COMMIT_ABORT);
 	if (ret < 0) {
 		ret = error(_("failed replaying descendants"));
 		goto out;
@@ -738,12 +981,14 @@ int cmd_history(int argc,
 		struct repository *repo)
 {
 	const char * const usage[] = {
+		GIT_HISTORY_FIXUP_USAGE,
 		GIT_HISTORY_REWORD_USAGE,
 		GIT_HISTORY_SPLIT_USAGE,
 		NULL,
 	};
 	parse_opt_subcommand_fn *fn = NULL;
 	struct option options[] = {
+		OPT_SUBCOMMAND("fixup", &fn, cmd_history_fixup),
 		OPT_SUBCOMMAND("reword", &fn, cmd_history_reword),
 		OPT_SUBCOMMAND("split", &fn, cmd_history_split),
 		OPT_END(),
diff --git a/builtin/hook.c b/builtin/hook.c
index c058558..cceeb35 100644
--- a/builtin/hook.c
+++ b/builtin/hook.c
@@ -4,23 +4,15 @@
 #include "environment.h"
 #include "gettext.h"
 #include "hook.h"
-#include "hook-list.h"
 #include "parse-options.h"
+#include "thread-utils.h"
 
 #define BUILTIN_HOOK_RUN_USAGE \
-	N_("git hook run [--allow-unknown-hook-name] [--ignore-missing] [--to-stdin=<path>] <hook-name> [-- <hook-args>]")
+	N_("git hook run [--allow-unknown-hook-name] [--ignore-missing] [--to-stdin=<path>] [(-j|--jobs) <n>]\n" \
+	   "<hook-name> [-- <hook-args>]")
 #define BUILTIN_HOOK_LIST_USAGE \
 	N_("git hook list [--allow-unknown-hook-name] [-z] [--show-scope] <hook-name>")
 
-static int is_known_hook(const char *name)
-{
-	const char **p;
-	for (p = hook_name_list; *p; p++)
-		if (!strcmp(*p, name))
-			return 1;
-	return 0;
-}
-
 static const char * const builtin_hook_usage[] = {
 	BUILTIN_HOOK_RUN_USAGE,
 	BUILTIN_HOOK_LIST_USAGE,
@@ -96,14 +88,22 @@ static int list(int argc, const char **argv, const char *prefix,
 			const char *name = h->u.configured.friendly_name;
 			const char *scope = show_scope ?
 				config_scope_name(h->u.configured.scope) : NULL;
+			/*
+			 * Show the most relevant disable reason. Event-level
+			 * takes precedence: if the whole event is off, that
+			 * is what the user needs to know. The per-hook
+			 * "disabled" surfaces once the event is re-enabled.
+			 */
+			const char *disability =
+				h->u.configured.event_disabled ? "event-disabled\t" :
+				h->u.configured.disabled       ? "disabled\t"       :
+								 "";
 			if (scope)
-				printf("%s\t%s%s%c", scope,
-				       h->u.configured.disabled ? "disabled\t" : "",
-				       name, line_terminator);
+				printf("%s\t%s%s%c", scope, disability, name,
+				       line_terminator);
 			else
-				printf("%s%s%c",
-				       h->u.configured.disabled ? "disabled\t" : "",
-				       name, line_terminator);
+				printf("%s%s%c", disability, name,
+				       line_terminator);
 			break;
 		}
 		default:
@@ -124,6 +124,7 @@ static int run(int argc, const char **argv, const char *prefix,
 	struct run_hooks_opt opt = RUN_HOOKS_OPT_INIT;
 	int ignore_missing = 0;
 	int allow_unknown = 0;
+	int jobs = 0;
 	const char *hook_name;
 	struct option run_options[] = {
 		OPT_BOOL(0, "allow-unknown-hook-name", &allow_unknown,
@@ -132,6 +133,8 @@ static int run(int argc, const char **argv, const char *prefix,
 			 N_("silently ignore missing requested <hook-name>")),
 		OPT_STRING(0, "to-stdin", &opt.path_to_stdin, N_("path"),
 			   N_("file to read into hooks' stdin")),
+		OPT_INTEGER('j', "jobs", &jobs,
+			    N_("run up to <n> hooks simultaneously (-1 for CPU count)")),
 		OPT_END(),
 	};
 	int ret;
@@ -140,6 +143,15 @@ static int run(int argc, const char **argv, const char *prefix,
 			     builtin_hook_run_usage,
 			     PARSE_OPT_KEEP_DASHDASH);
 
+	if (jobs == -1)
+		opt.jobs = online_cpus();
+	else if (jobs < 0)
+		die(_("invalid value for -j: %d"
+		     " (use -1 for CPU count or a"
+		     " positive integer)"), jobs);
+	else
+		opt.jobs = jobs;
+
 	if (!argc)
 		goto usage;
 
diff --git a/builtin/index-pack.c b/builtin/index-pack.c
index d1e4727..cf0bd82 100644
--- a/builtin/index-pack.c
+++ b/builtin/index-pack.c
@@ -37,7 +37,7 @@ static const char index_pack_usage[] =
 
 struct object_entry {
 	struct pack_idx_entry idx;
-	unsigned long size;
+	size_t size;
 	unsigned char hdr_size;
 	signed char type;
 	signed char real_type;
@@ -136,7 +136,7 @@ static int nr_threads;
 static int from_stdin;
 static int strict;
 static int do_fsck_object;
-static struct fsck_options fsck_options = FSCK_OPTIONS_MISSING_GITMODULES;
+static struct fsck_options fsck_options;
 static int verbose;
 static const char *progress_title;
 static int show_resolving_progress;
@@ -145,8 +145,7 @@ static int check_self_contained_and_connected;
 
 static struct progress *progress;
 
-/* We always read in 4kB chunks. */
-static unsigned char input_buffer[4096];
+static unsigned char input_buffer[DEFAULT_IO_BUFFER_SIZE];
 static unsigned int input_offset, input_len;
 static off_t consumed_bytes;
 static off_t max_input_size;
@@ -469,7 +468,7 @@ static int is_delta_type(enum object_type type)
 	return (type == OBJ_REF_DELTA || type == OBJ_OFS_DELTA);
 }
 
-static void *unpack_entry_data(off_t offset, unsigned long size,
+static void *unpack_entry_data(off_t offset, size_t size,
 			       enum object_type type, struct object_id *oid)
 {
 	static char fixed_buf[8192];
@@ -524,7 +523,7 @@ static void *unpack_raw_entry(struct object_entry *obj,
 			      struct object_id *oid)
 {
 	unsigned char *p;
-	unsigned long size, c;
+	size_t size, c;
 	off_t base_offset;
 	unsigned shift;
 	void *data;
@@ -539,6 +538,8 @@ static void *unpack_raw_entry(struct object_entry *obj,
 	size = (c & 15);
 	shift = 4;
 	while (c & 0x80) {
+		if ((bitsizeof(size_t) - 7) < shift)
+			die(_("object size too large for this platform"));
 		p = fill(1);
 		c = *p;
 		use(1);
@@ -891,7 +892,7 @@ static void sha1_object(const void *data, struct object_entry *obj_entry,
 	if (startup_info->have_repository) {
 		read_lock();
 		collision_test_needed = odb_has_object(the_repository->objects, oid,
-						       HAS_OBJECT_FETCH_PROMISOR);
+						       ODB_HAS_OBJECT_FETCH_PROMISOR);
 		read_unlock();
 	}
 
@@ -1908,6 +1909,8 @@ int cmd_index_pack(int argc,
 	show_usage_if_asked(argc, argv, index_pack_usage);
 
 	disable_replace_refs();
+
+	fsck_options_init(&fsck_options, the_repository, FSCK_OPTIONS_MISSING_GITMODULES);
 	fsck_options.walk = mark_link;
 
 	reset_pack_idx_option(&opts);
diff --git a/builtin/init-db.c b/builtin/init-db.c
index bb853e6..c55517a 100644
--- a/builtin/init-db.c
+++ b/builtin/init-db.c
@@ -237,9 +237,9 @@ int cmd_init_db(int argc,
 		if (!git_work_tree_cfg)
 			git_work_tree_cfg = xgetcwd();
 		if (work_tree)
-			set_git_work_tree(work_tree);
+			set_git_work_tree(the_repository, work_tree);
 		else
-			set_git_work_tree(git_work_tree_cfg);
+			set_git_work_tree(the_repository, git_work_tree_cfg);
 		if (access(repo_get_work_tree(the_repository), X_OK))
 			die_errno (_("Cannot access work tree '%s'"),
 				   repo_get_work_tree(the_repository));
@@ -248,11 +248,11 @@ int cmd_init_db(int argc,
 		if (real_git_dir)
 			die(_("--separate-git-dir incompatible with bare repository"));
 		if (work_tree)
-			set_git_work_tree(work_tree);
+			set_git_work_tree(the_repository, work_tree);
 	}
 
 	flags |= INIT_DB_EXIST_OK;
-	ret = init_db(git_dir, real_git_dir, template_dir, hash_algo,
+	ret = init_db(the_repository, git_dir, real_git_dir, template_dir, hash_algo,
 		      ref_storage_format, initial_branch,
 		      init_shared_repository, flags);
 
diff --git a/builtin/log.c b/builtin/log.c
index 8c0939d..e464b30 100644
--- a/builtin/log.c
+++ b/builtin/log.c
@@ -21,10 +21,12 @@
 #include "color.h"
 #include "commit.h"
 #include "diff.h"
+#include "diffcore.h"
 #include "diff-merges.h"
 #include "revision.h"
 #include "log-tree.h"
 #include "oid-array.h"
+#include "oidset.h"
 #include "tag.h"
 #include "reflog-walk.h"
 #include "patch-ids.h"
@@ -43,9 +45,11 @@
 #include "utf8.h"
 
 #include "commit-reach.h"
+#include "promisor-remote.h"
 #include "range-diff.h"
 #include "tmp-objdir.h"
 #include "tree.h"
+#include "userdiff.h"
 #include "write-or-die.h"
 
 #define MAIL_DEFAULT_WRAP 72
@@ -2602,6 +2606,131 @@ static void print_commit(char sign, struct commit *commit, int verbose,
 	}
 }
 
+/*
+ * Enumerate blob OIDs from a single commit's diff, inserting them into blobs.
+ * Skips files whose userdiff driver explicitly declares binary status
+ * (drv->binary > 0), since patch-ID uses oid_to_hex() for those and
+ * never reads blob content.  Use userdiff_find_by_path() since
+ * diff_filespec_load_driver() is static in diff.c.
+ *
+ * Clean up with diff_queue_clear() (from diffcore.h).
+ */
+static void collect_diff_blob_oids(struct commit *commit,
+				   struct diff_options *opts,
+				   struct oidset *blobs)
+{
+	struct diff_queue_struct *q;
+
+	/*
+	 * Merge commits are filtered out by patch_id_defined() in patch-ids.c,
+	 * so we'll never be called with one.
+	 */
+	assert(!commit->parents || !commit->parents->next);
+
+	if (commit->parents)
+		diff_tree_oid(&commit->parents->item->object.oid,
+			      &commit->object.oid, "", opts);
+	else
+		diff_root_tree_oid(&commit->object.oid, "", opts);
+	diffcore_std(opts);
+
+	q = &diff_queued_diff;
+	for (int i = 0; i < q->nr; i++) {
+		struct diff_filepair *p = q->queue[i];
+		struct userdiff_driver *drv;
+
+		/* Skip binary files */
+		drv = userdiff_find_by_path(opts->repo->index, p->one->path);
+		if (drv && drv->binary > 0)
+			continue;
+
+		if (DIFF_FILE_VALID(p->one) &&
+		    odb_read_object_info_extended(opts->repo->objects,
+						  &p->one->oid, NULL,
+						  OBJECT_INFO_FOR_PREFETCH))
+			oidset_insert(blobs, &p->one->oid);
+		if (DIFF_FILE_VALID(p->two) &&
+		    odb_read_object_info_extended(opts->repo->objects,
+						  &p->two->oid, NULL,
+						  OBJECT_INFO_FOR_PREFETCH))
+			oidset_insert(blobs, &p->two->oid);
+	}
+	diff_queue_clear(q);
+}
+
+static int always_match(const void *cmp_data UNUSED,
+			const struct hashmap_entry *entry1 UNUSED,
+			const struct hashmap_entry *entry2 UNUSED,
+			const void *keydata UNUSED)
+{
+	return 0;
+}
+
+/*
+ * Prefetch blobs for git cherry in partial clones.
+ *
+ * Called between the revision walk (which builds the head-side
+ * commit list) and the has_commit_patch_id() comparison loop.
+ *
+ * Uses a cmpfn-swap trick to avoid reading blobs: temporarily
+ * replaces the hashmap's comparison function with a trivial
+ * always-match function, so hashmap_get()/hashmap_get_next() match
+ * any entry with the same oidhash bucket.  These are the set of oids
+ * that would trigger patch_id_neq() during normal lookup and cause
+ * blobs to be read on demand, and we want to prefetch them all at
+ * once instead.
+ */
+static void prefetch_cherry_blobs(struct repository *repo,
+				  struct commit_list *list,
+				  struct patch_ids *ids)
+{
+	struct oidset blobs = OIDSET_INIT;
+	hashmap_cmp_fn original_cmpfn;
+
+	/* Exit if we're not in a partial clone */
+	if (!repo_has_promisor_remote(repo))
+		return;
+
+	/* Save original cmpfn, replace with always_match */
+	original_cmpfn = ids->patches.cmpfn;
+	ids->patches.cmpfn = always_match;
+
+	/* Find header-only collisions, gather blobs from those commits */
+	for (struct commit_list *l = list; l; l = l->next) {
+		struct commit *c = l->item;
+		bool match_found = false;
+		for (struct patch_id *cur = patch_id_iter_first(c, ids);
+		     cur;
+		     cur = patch_id_iter_next(cur, ids)) {
+			match_found = true;
+			collect_diff_blob_oids(cur->commit, &ids->diffopts,
+					       &blobs);
+		}
+		if (match_found)
+			collect_diff_blob_oids(c, &ids->diffopts, &blobs);
+	}
+
+	/* Restore original cmpfn */
+	ids->patches.cmpfn = original_cmpfn;
+
+	/* If we have any blobs to fetch, fetch them */
+	if (oidset_size(&blobs)) {
+		struct oid_array to_fetch = OID_ARRAY_INIT;
+		struct oidset_iter iter;
+		const struct object_id *oid;
+
+		oidset_iter_init(&blobs, &iter);
+		while ((oid = oidset_iter_next(&iter)))
+			oid_array_append(&to_fetch, oid);
+
+		promisor_remote_get_direct(repo, to_fetch.oid, to_fetch.nr);
+
+		oid_array_clear(&to_fetch);
+	}
+
+	oidset_clear(&blobs);
+}
+
 int cmd_cherry(int argc,
 	       const char **argv,
 	       const char *prefix,
@@ -2673,6 +2802,8 @@ int cmd_cherry(int argc,
 		commit_list_insert(commit, &list);
 	}
 
+	prefetch_cherry_blobs(the_repository, list, &ids);
+
 	for (struct commit_list *l = list; l; l = l->next) {
 		char sign = '+';
 
diff --git a/builtin/ls-files.c b/builtin/ls-files.c
index b148607..e1a22b4 100644
--- a/builtin/ls-files.c
+++ b/builtin/ls-files.c
@@ -703,8 +703,8 @@ int cmd_ls_files(int argc,
 	if (dir.exclude_per_dir)
 		exc_given = 1;
 
-	if (require_work_tree && !is_inside_work_tree())
-		setup_work_tree();
+	if (require_work_tree && !is_inside_work_tree(repo))
+		setup_work_tree(repo);
 
 	if (recurse_submodules &&
 	    (show_deleted || show_others || show_unmerged ||
diff --git a/builtin/merge-base.c b/builtin/merge-base.c
index c7ee97f..a87011c 100644
--- a/builtin/merge-base.c
+++ b/builtin/merge-base.c
@@ -11,10 +11,12 @@
 
 static int show_merge_base(struct commit **rev, size_t rev_nr, int show_all)
 {
+	enum merge_base_flags flags = show_all ? MERGE_BASE_FIND_ALL : 0;
 	struct commit_list *result = NULL, *r;
 
 	if (repo_get_merge_bases_many_dirty(the_repository, rev[0],
-					    rev_nr - 1, rev + 1, &result) < 0) {
+					    rev_nr - 1, rev + 1,
+					    flags, &result) < 0) {
 		commit_list_free(result);
 		return -1;
 	}
diff --git a/builtin/merge-file.c b/builtin/merge-file.c
index 59a9792..8fa5765 100644
--- a/builtin/merge-file.c
+++ b/builtin/merge-file.c
@@ -110,7 +110,7 @@ int cmd_merge_file(int argc,
 
 	if (!repo && object_id)
 		/* emit the correct "not a git repo" error in this case */
-		setup_git_directory();
+		setup_git_directory(the_repository);
 
 	for (i = 0; i < 3; i++) {
 		char *fname;
diff --git a/builtin/merge.c b/builtin/merge.c
index 2cbce56..5b46a59 100644
--- a/builtin/merge.c
+++ b/builtin/merge.c
@@ -537,7 +537,8 @@ static void finish(struct commit *head_commit,
 	run_hooks_l(the_repository, "post-merge", squash ? "1" : "0", NULL);
 
 	if (new_head)
-		apply_autostash_ref(the_repository, "MERGE_AUTOSTASH");
+		apply_autostash_ref(the_repository, "MERGE_AUTOSTASH",
+				    NULL, NULL, NULL, NULL);
 	strbuf_release(&reflog_message);
 }
 
@@ -1672,12 +1673,14 @@ int cmd_merge(int argc,
 		}
 
 		if (autostash)
-			create_autostash_ref(the_repository, "MERGE_AUTOSTASH");
+			create_autostash_ref(the_repository, "MERGE_AUTOSTASH",
+					     NULL, false);
 		if (checkout_fast_forward(the_repository,
 					  &head_commit->object.oid,
 					  &commit->object.oid,
 					  overwrite_ignore)) {
-			apply_autostash_ref(the_repository, "MERGE_AUTOSTASH");
+			apply_autostash_ref(the_repository, "MERGE_AUTOSTASH",
+					    NULL, NULL, NULL, NULL);
 			ret = 1;
 			goto done;
 		}
@@ -1735,21 +1738,11 @@ int cmd_merge(int argc,
 		struct commit_list *j;
 
 		for (j = remoteheads; j; j = j->next) {
-			struct commit_list *common_one = NULL;
-			struct commit *common_item;
-
-			/*
-			 * Here we *have* to calculate the individual
-			 * merge_bases again, otherwise "git merge HEAD^
-			 * HEAD^^" would be missed.
-			 */
-			if (repo_get_merge_bases(the_repository, head_commit,
-						 j->item, &common_one) < 0)
+			int ret = repo_in_merge_bases(the_repository,
+						      j->item, head_commit);
+			if (ret < 0)
 				exit(128);
-
-			common_item = common_one->item;
-			commit_list_free(common_one);
-			if (!oideq(&common_item->object.oid, &j->item->object.oid)) {
+			if (!ret) {
 				up_to_date = 0;
 				break;
 			}
@@ -1764,7 +1757,8 @@ int cmd_merge(int argc,
 		die_ff_impossible();
 
 	if (autostash)
-		create_autostash_ref(the_repository, "MERGE_AUTOSTASH");
+		create_autostash_ref(the_repository, "MERGE_AUTOSTASH",
+				     NULL, false);
 
 	/* We are going to make a new commit. */
 	git_committer_info(IDENT_STRICT);
@@ -1849,7 +1843,8 @@ int cmd_merge(int argc,
 		else
 			fprintf(stderr, _("Merge with strategy %s failed.\n"),
 				use_strategies[0]->name);
-		apply_autostash_ref(the_repository, "MERGE_AUTOSTASH");
+		apply_autostash_ref(the_repository, "MERGE_AUTOSTASH",
+				    NULL, NULL, NULL, NULL);
 		ret = 2;
 		goto done;
 	} else if (best_strategy == wt_strategy)
diff --git a/builtin/mktag.c b/builtin/mktag.c
index 7cf6e12..f40264a 100644
--- a/builtin/mktag.c
+++ b/builtin/mktag.c
@@ -16,7 +16,7 @@ static char const * const builtin_mktag_usage[] = {
 };
 static int option_strict = 1;
 
-static struct fsck_options fsck_options = FSCK_OPTIONS_STRICT;
+static struct fsck_options fsck_options;
 
 static int mktag_fsck_error_func(struct fsck_options *o UNUSED,
 				 void *fsck_report UNUSED,
@@ -75,7 +75,7 @@ static int verify_object_in_tag(struct object_id *tagged_oid, int *tagged_type)
 int cmd_mktag(int argc,
 	      const char **argv,
 	      const char *prefix,
-	      struct repository *repo UNUSED)
+	      struct repository *repo)
 {
 	static struct option builtin_mktag_options[] = {
 		OPT_BOOL(0, "strict", &option_strict,
@@ -94,6 +94,7 @@ int cmd_mktag(int argc,
 	if (strbuf_read(&buf, 0, 0) < 0)
 		die_errno(_("could not read from stdin"));
 
+	fsck_options_init(&fsck_options, repo, FSCK_OPTIONS_STRICT);
 	fsck_options.error_func = mktag_fsck_error_func;
 	fsck_set_msg_type_from_ids(&fsck_options, FSCK_MSG_EXTRA_HEADER_ENTRY,
 				   FSCK_WARN);
diff --git a/builtin/multi-pack-index.c b/builtin/multi-pack-index.c
index 0f72d96..00ffb36 100644
--- a/builtin/multi-pack-index.c
+++ b/builtin/multi-pack-index.c
@@ -16,11 +16,13 @@
 #define BUILTIN_MIDX_WRITE_USAGE \
 	N_("git multi-pack-index [<options>] write [--preferred-pack=<pack>]\n" \
 	   "  [--[no-]bitmap] [--[no-]incremental] [--[no-]stdin-packs]\n" \
-	   "  [--refs-snapshot=<path>]")
+	   "  [--refs-snapshot=<path>] [--[no-]write-chain-file]\n" \
+	   "  [--base=<checksum>]")
 
 #define BUILTIN_MIDX_COMPACT_USAGE \
 	N_("git multi-pack-index [<options>] compact [--[no-]incremental]\n" \
-	   "  [--[no-]bitmap] <from> <to>")
+	   "  [--[no-]bitmap] [--base=<checksum>] [--[no-]write-chain-file]\n" \
+	   "  <from> <to>")
 
 #define BUILTIN_MIDX_VERIFY_USAGE \
 	N_("git multi-pack-index [<options>] verify")
@@ -63,6 +65,7 @@ static char const * const builtin_multi_pack_index_usage[] = {
 static struct opts_multi_pack_index {
 	char *object_dir;
 	const char *preferred_pack;
+	const char *incremental_base;
 	char *refs_snapshot;
 	unsigned long batch_size;
 	unsigned flags;
@@ -151,8 +154,13 @@ static int cmd_multi_pack_index_write(int argc, const char **argv,
 			   N_("pack for reuse when computing a multi-pack bitmap")),
 		OPT_BIT(0, "bitmap", &opts.flags, N_("write multi-pack bitmap"),
 			MIDX_WRITE_BITMAP | MIDX_WRITE_REV_INDEX),
+		OPT_STRING(0, "base", &opts.incremental_base, N_("checksum"),
+			   N_("base MIDX for incremental writes")),
 		OPT_BIT(0, "incremental", &opts.flags,
 			N_("write a new incremental MIDX"), MIDX_WRITE_INCREMENTAL),
+		OPT_NEGBIT(0, "write-chain-file", &opts.flags,
+			N_("write the multi-pack-index chain file"),
+			MIDX_WRITE_NO_CHAIN),
 		OPT_BOOL(0, "stdin-packs", &opts.stdin_packs,
 			 N_("write multi-pack index containing only given indexes")),
 		OPT_FILENAME(0, "refs-snapshot", &opts.refs_snapshot,
@@ -178,6 +186,22 @@ static int cmd_multi_pack_index_write(int argc, const char **argv,
 	if (argc)
 		usage_with_options(builtin_multi_pack_index_write_usage,
 				   options);
+
+	if (opts.flags & MIDX_WRITE_NO_CHAIN &&
+	    !(opts.flags & MIDX_WRITE_INCREMENTAL)) {
+		error(_("cannot use %s without %s"),
+		      "--no-write-chain-file", "--incremental");
+		usage_with_options(builtin_multi_pack_index_write_usage,
+				   options);
+	}
+
+	if (opts.incremental_base &&
+	    !(opts.flags & MIDX_WRITE_NO_CHAIN)) {
+		error(_("cannot use --base without --no-write-chain-file"));
+		usage_with_options(builtin_multi_pack_index_write_usage,
+				   options);
+	}
+
 	source = handle_object_dir_option(repo);
 
 	FREE_AND_NULL(options);
@@ -189,7 +213,8 @@ static int cmd_multi_pack_index_write(int argc, const char **argv,
 
 		ret = write_midx_file_only(source, &packs,
 					   opts.preferred_pack,
-					   opts.refs_snapshot, opts.flags);
+					   opts.refs_snapshot,
+					   opts.incremental_base, opts.flags);
 
 		string_list_clear(&packs, 0);
 		free(opts.refs_snapshot);
@@ -217,10 +242,15 @@ static int cmd_multi_pack_index_compact(int argc, const char **argv,
 
 	struct option *options;
 	static struct option builtin_multi_pack_index_compact_options[] = {
+		OPT_STRING(0, "base", &opts.incremental_base, N_("checksum"),
+			   N_("base MIDX for incremental writes")),
 		OPT_BIT(0, "bitmap", &opts.flags, N_("write multi-pack bitmap"),
 			MIDX_WRITE_BITMAP | MIDX_WRITE_REV_INDEX),
 		OPT_BIT(0, "incremental", &opts.flags,
 			N_("write a new incremental MIDX"), MIDX_WRITE_INCREMENTAL),
+		OPT_NEGBIT(0, "write-chain-file", &opts.flags,
+			N_("write the multi-pack-index chain file"),
+			MIDX_WRITE_NO_CHAIN),
 		OPT_END(),
 	};
 
@@ -239,6 +269,15 @@ static int cmd_multi_pack_index_compact(int argc, const char **argv,
 	if (argc != 2)
 		usage_with_options(builtin_multi_pack_index_compact_usage,
 				   options);
+
+	if (opts.flags & MIDX_WRITE_NO_CHAIN &&
+	    !(opts.flags & MIDX_WRITE_INCREMENTAL)) {
+		error(_("cannot use %s without %s"),
+		      "--no-write-chain-file", "--incremental");
+		usage_with_options(builtin_multi_pack_index_compact_usage,
+				   options);
+	}
+
 	source = handle_object_dir_option(the_repository);
 
 	FREE_AND_NULL(options);
@@ -266,7 +305,8 @@ static int cmd_multi_pack_index_compact(int argc, const char **argv,
 			die(_("MIDX %s must be an ancestor of %s"), argv[0], argv[1]);
 	}
 
-	ret = write_midx_file_compact(source, from_midx, to_midx, opts.flags);
+	ret = write_midx_file_compact(source, from_midx, to_midx,
+				      opts.incremental_base, opts.flags);
 
 	return ret;
 }
diff --git a/builtin/mv.c b/builtin/mv.c
index 2215d34..948b330 100644
--- a/builtin/mv.c
+++ b/builtin/mv.c
@@ -71,7 +71,7 @@ static void internal_prefix_pathspec(struct strvec *out,
 
 		trimmed = xmemdupz(pathspec[i], to_copy);
 		maybe_basename = (flags & DUP_BASENAME) ? basename(trimmed) : trimmed;
-		prefixed_path = prefix_path(prefix, prefixlen, maybe_basename);
+		prefixed_path = prefix_path(the_repository, prefix, prefixlen, maybe_basename);
 		strvec_push(out, prefixed_path);
 
 		free(prefixed_path);
@@ -394,7 +394,8 @@ int cmd_mv(int argc,
 			for (j = 0; j < last - first; j++) {
 				const struct cache_entry *ce = the_repository->index->cache[first + j];
 				const char *path = ce->name;
-				char *prefixed_path = prefix_path(dst_with_slash, dst_with_slash_len, path + length + 1);
+				char *prefixed_path = prefix_path(the_repository, dst_with_slash,
+								  dst_with_slash_len, path + length + 1);
 
 				strvec_push(&sources, path);
 				strvec_push(&destinations, prefixed_path);
diff --git a/builtin/name-rev.c b/builtin/name-rev.c
index d6594ad..60cbbfb 100644
--- a/builtin/name-rev.c
+++ b/builtin/name-rev.c
@@ -17,6 +17,10 @@
 #include "commit-graph.h"
 #include "wildmatch.h"
 #include "mem-pool.h"
+#include "pretty.h"
+#include "revision.h"
+#include "notes.h"
+#include "write-or-die.h"
 
 /*
  * One day.  See the 'name a rev shortly after epoch' test in t6120 when
@@ -270,6 +274,43 @@ struct name_ref_data {
 	struct string_list exclude_filters;
 };
 
+struct pretty_format {
+	struct pretty_print_context ctx;
+	struct userformat_want want;
+};
+
+enum command_type {
+	NAME_REV = 1,
+	FORMAT_REV = 2,
+};
+
+enum stdin_mode {
+    TEXT = 1,
+    REVS = 2,
+};
+
+struct command {
+	enum command_type type;
+	union {
+		int name_only;
+		struct pretty_format *pretty_format;
+	} u;
+};
+
+static void init_name_rev_command(struct command *cmd,
+				  int name_only)
+{
+	cmd->type = NAME_REV;
+	cmd->u.name_only = name_only;
+}
+
+static void init_format_rev_command(struct command *cmd,
+				    struct pretty_format *pretty_format)
+{
+	cmd->type = FORMAT_REV;
+	cmd->u.pretty_format = pretty_format;
+}
+
 static struct tip_table {
 	struct tip_table_entry {
 		struct object_id oid;
@@ -464,9 +505,9 @@ static const char *get_rev_name(const struct object *o, struct strbuf *buf)
 	if (!n)
 		return NULL;
 
-	if (!n->generation)
+	if (!n->generation) {
 		return n->tip_name;
-	else {
+	} else {
 		strbuf_reset(buf);
 		strbuf_addstr(buf, n->tip_name);
 		strbuf_strip_suffix(buf, "^0");
@@ -475,6 +516,27 @@ static const char *get_rev_name(const struct object *o, struct strbuf *buf)
 	}
 }
 
+static const char *get_format_rev(const struct commit *c,
+				  struct pretty_format *format_ctx,
+				  struct strbuf *buf)
+{
+	strbuf_reset(buf);
+
+	if (format_ctx->want.notes) {
+		struct strbuf notebuf = STRBUF_INIT;
+
+		format_display_notes(&c->object.oid, &notebuf,
+				     get_log_output_encoding(),
+				     format_ctx->ctx.fmt == CMIT_FMT_USERFORMAT);
+		format_ctx->ctx.notes_message = strbuf_detach(&notebuf, NULL);
+	}
+
+	pretty_print_commit(&format_ctx->ctx, c, buf);
+	FREE_AND_NULL(format_ctx->ctx.notes_message);
+
+	return buf->buf;
+}
+
 static void show_name(const struct object *obj,
 		      const char *caller_name,
 		      int always, int allow_undefined, int name_only)
@@ -505,7 +567,7 @@ static char const * const name_rev_usage[] = {
 	NULL
 };
 
-static void name_rev_line(char *p, struct name_ref_data *data)
+static void name_rev_line(char *p, struct command *cmd)
 {
 	struct strbuf buf = STRBUF_INIT;
 	int counter = 0;
@@ -514,33 +576,52 @@ static void name_rev_line(char *p, struct name_ref_data *data)
 
 	for (p_start = p; *p; p++) {
 #define ishex(x) (isdigit((x)) || ((x) >= 'a' && (x) <= 'f'))
-		if (!ishex(*p))
+		if (!ishex(*p)) {
 			counter = 0;
-		else if (++counter == hexsz &&
-			 !ishex(*(p+1))) {
+		} else if (++counter == hexsz &&
+			   !ishex(*(p + 1))) {
 			struct object_id oid;
 			const char *name = NULL;
-			char c = *(p+1);
+			char c = *(p + 1);
 			int p_len = p - p_start + 1;
+			struct object *o = NULL;
+			int oid_ret = 1;
 
 			counter = 0;
 
-			*(p+1) = 0;
-			if (!repo_get_oid(the_repository, p - (hexsz - 1), &oid)) {
-				struct object *o =
-					lookup_object(the_repository, &oid);
+			*(p + 1) = 0;
+			oid_ret = repo_get_oid(the_repository, p - (hexsz - 1), &oid);
+			*(p + 1) = c;
+
+			switch (cmd->type) {
+			case NAME_REV:
+				if (!oid_ret)
+					o = lookup_object(the_repository, &oid);
 				if (o)
 					name = get_rev_name(o, &buf);
+				if (!name)
+					continue;
+				if (cmd->u.name_only)
+					printf("%.*s%s", p_len - hexsz, p_start, name);
+				else
+					printf("%.*s (%s)", p_len, p_start, name);
+				break;
+			case FORMAT_REV:
+				if (!oid_ret)
+					o = parse_object(the_repository, &oid);
+				if (o && o->type == OBJ_COMMIT)
+					name = get_format_rev((const struct commit *)o,
+							      cmd->u.pretty_format,
+							      &buf);
+				if (name)
+					printf("%.*s%s", p_len - hexsz, p_start, name);
+				else
+					printf("%.*s", p_len, p_start);
+				break;
+			default:
+				BUG("uncovered case: %d", cmd->type);
 			}
-			*(p+1) = c;
 
-			if (!name)
-				continue;
-
-			if (data->name_only)
-				printf("%.*s%s", p_len - hexsz, p_start, name);
-			else
-				printf("%.*s (%s)", p_len, p_start, name);
 			p_start = p + 1;
 		}
 	}
@@ -565,13 +646,14 @@ int cmd_name_rev(int argc,
 #endif
 	int all = 0, annotate_stdin = 0, allow_undefined = 1, always = 0, peel_tag = 0;
 	struct name_ref_data data = { 0, 0, STRING_LIST_INIT_NODUP, STRING_LIST_INIT_NODUP };
+	struct command cmd;
 	struct option opts[] = {
 		OPT_BOOL(0, "name-only", &data.name_only, N_("print only ref-based names (no object names)")),
 		OPT_BOOL(0, "tags", &data.tags_only, N_("only use tags to name the commits")),
 		OPT_STRING_LIST(0, "refs", &data.ref_filters, N_("pattern"),
-				   N_("only use refs matching <pattern>")),
+				N_("only use refs matching <pattern>")),
 		OPT_STRING_LIST(0, "exclude", &data.exclude_filters, N_("pattern"),
-				   N_("ignore refs matching <pattern>")),
+				N_("ignore refs matching <pattern>")),
 		OPT_GROUP(""),
 		OPT_BOOL(0, "all", &all, N_("list all commits reachable from all refs")),
 #ifndef WITH_BREAKING_CHANGES
@@ -583,10 +665,10 @@ int cmd_name_rev(int argc,
 #endif /* WITH_BREAKING_CHANGES */
 		OPT_BOOL(0, "annotate-stdin", &annotate_stdin, N_("annotate text from stdin")),
 		OPT_BOOL(0, "undefined", &allow_undefined, N_("allow to print `undefined` names (default)")),
-		OPT_BOOL(0, "always",     &always,
-			   N_("show abbreviated commit object as fallback")),
+		OPT_BOOL(0, "always", &always,
+			 N_("show abbreviated commit object as fallback")),
 		OPT_HIDDEN_BOOL(0, "peel-tag", &peel_tag,
-			   N_("dereference tags in the input (internal use)")),
+				N_("dereference tags in the input (internal use)")),
 		OPT_END(),
 	};
 
@@ -594,6 +676,7 @@ int cmd_name_rev(int argc,
 	init_commit_rev_name(&rev_names);
 	repo_config(the_repository, git_default_config, NULL);
 	argc = parse_options(argc, argv, prefix, opts, name_rev_usage, 0);
+	init_name_rev_command(&cmd, data.name_only);
 
 #ifndef WITH_BREAKING_CHANGES
 	if (transform_stdin) {
@@ -661,7 +744,7 @@ int cmd_name_rev(int argc,
 
 		while (strbuf_getline(&sb, stdin) != EOF) {
 			strbuf_addch(&sb, '\n');
-			name_rev_line(sb.buf, &data);
+			name_rev_line(sb.buf, &cmd);
 		}
 		strbuf_release(&sb);
 	} else if (all) {
@@ -688,3 +771,170 @@ int cmd_name_rev(int argc,
 	object_array_clear(&revs);
 	return 0;
 }
+
+struct format_nul_data {
+	bool nul_input;
+	bool nul_output;
+};
+
+static int format_nul_cb(const struct option *option,
+			 const char *arg,
+			 int unset)
+{
+	struct format_nul_data *data = option->value;
+	data->nul_input = 1;
+	data->nul_output = 1;
+	BUG_ON_OPT_NEG(unset);
+	BUG_ON_OPT_ARG(arg);
+	return 0;
+}
+
+static enum stdin_mode parse_stdin_mode(const char *stdin_mode)
+{
+	if (!strcmp(stdin_mode, "text"))
+		return TEXT;
+	else if (!strcmp(stdin_mode, "revs") ||
+		 !strcmp(stdin_mode, "rev"))
+		return REVS;
+	else
+		die(_("'%s' needs to be either text, revs, or rev"),
+		    "--stdin-mode");
+}
+
+static char const *const format_rev_usage[] = {
+	N_("(EXPERIMENTAL!) git format-rev --stdin-mode=<mode> "
+	   "--format=<pretty> [--[no-]notes=<ref>] "
+	   "[-z] [--[no-]null-output] [--[no-]null-input]"),
+	NULL
+};
+
+int cmd_format_rev(int argc,
+		   const char **argv,
+		   const char *prefix,
+		   struct repository *repo UNUSED)
+{
+	const char *format = NULL;
+	enum stdin_mode stdin_mode;
+	const char *stdin_mode_arg = NULL;
+	struct format_nul_data nul_data = { 0, 0 };
+	char output_terminator;
+	strbuf_getline_fn getline_fn;
+	struct display_notes_opt format_notes_opt;
+	struct rev_info format_rev = REV_INFO_INIT;
+	struct pretty_format format_pp = { 0 };
+	struct string_list notes = STRING_LIST_INIT_NODUP;
+	struct strbuf scratch_buf = STRBUF_INIT;
+	struct command cmd;
+	struct option opts[] = {
+		OPT_STRING(0, "format", &format, N_("format"),
+			   N_("pretty format to use")),
+		OPT_STRING(0, "stdin-mode", &stdin_mode_arg, N_("stdin-mode"),
+			   N_("how revs are processed")),
+		OPT_STRING_LIST(0, "notes", &notes, N_("notes"),
+				N_("display notes for pretty format")),
+		OPT_CALLBACK_F('z', "null", &nul_data, N_("z"),
+			       N_("Use NUL for input and output termination"),
+			       PARSE_OPT_NOARG | PARSE_OPT_NONEG, format_nul_cb),
+		OPT_BOOL(0, "null-input", &nul_data.nul_input,
+			 N_("Use NUL for input termination")),
+		OPT_BOOL(0, "null-output", &nul_data.nul_output,
+			 N_("Use NUL for output termination")),
+		OPT_END(),
+	};
+
+	argc = parse_options(argc, argv, prefix, opts, format_rev_usage, 0);
+
+	if (argc > 0) {
+		error(_("too many arguments"));
+		usage_with_options(format_rev_usage, opts);
+	}
+
+	if (!format)
+		die(_("'%s' is required"), "--format");
+	if (!stdin_mode_arg)
+		die(_("'%s' is required"), "--stdin-mode");
+
+	getline_fn = nul_data.nul_input ? strbuf_getline_nul : strbuf_getline_lf;
+	output_terminator = nul_data.nul_output ? '\0' : '\n';
+
+	init_display_notes(&format_notes_opt);
+	stdin_mode = parse_stdin_mode(stdin_mode_arg);
+
+	get_commit_format(format, &format_rev);
+	format_pp.ctx.rev = &format_rev;
+	format_pp.ctx.fmt = format_rev.commit_format;
+	format_pp.ctx.abbrev = format_rev.abbrev;
+	format_pp.ctx.date_mode_explicit = format_rev.date_mode_explicit;
+	format_pp.ctx.date_mode = format_rev.date_mode;
+	format_pp.ctx.color = GIT_COLOR_AUTO;
+
+	userformat_find_requirements(format,
+				     &format_pp.want);
+	if (format_pp.want.notes) {
+		int ignore_show_notes = 0;
+		struct string_list_item *n;
+
+		for_each_string_list_item(n, &notes)
+			enable_ref_display_notes(&format_notes_opt,
+						 &ignore_show_notes,
+						 n->string);
+		load_display_notes(&format_notes_opt);
+	}
+
+	init_format_rev_command(&cmd, &format_pp);
+
+	switch (stdin_mode) {
+	case TEXT:
+		while (getline_fn(&scratch_buf, stdin) != EOF) {
+			name_rev_line(scratch_buf.buf, &cmd);
+			/*
+			 * We do not pass on the terminator to name_rev_line,
+			 * unlike name-rev.
+			 */
+			printf("%c", output_terminator);
+			maybe_flush_or_die(stdout, "stdout");
+		}
+		break;
+	case REVS:
+		while (getline_fn(&scratch_buf, stdin) != EOF) {
+			struct object_id oid;
+			struct object *object;
+			struct object *peeled;
+
+			if (repo_get_oid(the_repository, scratch_buf.buf, &oid)) {
+				fprintf(stderr, "Could not get object name for %s. Skipping.\n",
+					scratch_buf.buf);
+				continue;
+			}
+
+			object = parse_object(the_repository, &oid);
+			if (!object) {
+				fprintf(stderr, "Could not get object for %s. Skipping.\n",
+					scratch_buf.buf);
+				continue;
+			}
+
+			peeled = deref_tag(the_repository, object, scratch_buf.buf, 0);
+			if (!peeled || peeled->type != OBJ_COMMIT) {
+				fprintf(stderr,
+					"Could not get commit for %s. Skipping.\n",
+					scratch_buf.buf);
+				continue;
+			}
+
+			get_format_rev((struct commit *)peeled,
+				       &format_pp, &scratch_buf);
+			printf("%s%c", scratch_buf.buf, output_terminator);
+			maybe_flush_or_die(stdout, "stdout");
+			strbuf_release(&scratch_buf);
+		}
+		break;
+	default:
+		BUG("uncovered case: %d", stdin_mode);
+	}
+
+	strbuf_release(&scratch_buf);
+	string_list_clear(&notes, 0);
+	release_display_notes(&format_notes_opt);
+	return 0;
+}
diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c
index da10879..fe9fbec 100644
--- a/builtin/pack-objects.c
+++ b/builtin/pack-objects.c
@@ -28,6 +28,7 @@
 #include "reachable.h"
 #include "oid-array.h"
 #include "strvec.h"
+#include "strmap.h"
 #include "list.h"
 #include "packfile.h"
 #include "object-file.h"
@@ -217,6 +218,7 @@ static int have_non_local_packs;
 static int incremental;
 static int ignore_packed_keep_on_disk;
 static int ignore_packed_keep_in_core;
+static int ignore_packed_keep_in_core_open;
 static int ignore_packed_keep_in_core_has_cruft;
 static int allow_ofs_delta;
 static struct pack_idx_option pack_idx_opts;
@@ -627,14 +629,21 @@ static off_t write_reuse_object(struct hashfile *f, struct object_entry *entry,
 	struct packed_git *p = IN_PACK(entry);
 	struct pack_window *w_curs = NULL;
 	uint32_t pos;
-	off_t offset;
+	off_t offset, cur;
 	enum object_type type = oe_type(entry);
+	enum object_type in_pack_type;
 	off_t datalen;
 	unsigned char header[MAX_PACK_OBJECT_HEADER],
 		      dheader[MAX_PACK_OBJECT_HEADER];
 	unsigned hdrlen;
 	const unsigned hashsz = the_hash_algo->rawsz;
-	unsigned long entry_size = SIZE(entry);
+	size_t entry_size;
+
+	cur = entry->in_pack_offset;
+	in_pack_type = unpack_object_header(p, &w_curs, &cur, &entry_size);
+	if (in_pack_type < 0)
+		die(_("write_reuse_object: unable to parse object header of %s"),
+		    oid_to_hex(&entry->idx.oid));
 
 	if (DELTA(entry))
 		type = (allow_ofs_delta && DELTA(entry)->idx.offset) ?
@@ -662,7 +671,8 @@ static off_t write_reuse_object(struct hashfile *f, struct object_entry *entry,
 	datalen -= entry->in_pack_header_size;
 
 	if (!pack_to_stdout && p->index_version == 1 &&
-	    check_pack_inflate(p, &w_curs, offset, datalen, entry_size)) {
+	    check_pack_inflate(p, &w_curs, offset, datalen,
+			       cast_size_t_to_ulong(entry_size))) {
 		error(_("corrupt packed object for %s"),
 		      oid_to_hex(&entry->idx.oid));
 		unuse_pack(&w_curs);
@@ -1085,7 +1095,7 @@ static void write_reused_pack_one(struct packed_git *reuse_packfile,
 {
 	off_t offset, next, cur;
 	enum object_type type;
-	unsigned long size;
+	size_t size;
 
 	offset = pack_pos_to_offset(reuse_packfile, pos);
 	next = pack_pos_to_offset(reuse_packfile, pos + 1);
@@ -1632,7 +1642,8 @@ static int want_found_object(const struct object_id *oid, int exclude,
 	/*
 	 * Then handle .keep first, as we have a fast(er) path there.
 	 */
-	if (ignore_packed_keep_on_disk || ignore_packed_keep_in_core) {
+	if (ignore_packed_keep_on_disk || ignore_packed_keep_in_core ||
+	    ignore_packed_keep_in_core_open) {
 		/*
 		 * Set the flags for the kept-pack cache to be the ones we want
 		 * to ignore.
@@ -1646,6 +1657,8 @@ static int want_found_object(const struct object_id *oid, int exclude,
 			flags |= KEPT_PACK_ON_DISK;
 		if (ignore_packed_keep_in_core)
 			flags |= KEPT_PACK_IN_CORE;
+		if (ignore_packed_keep_in_core_open)
+			flags |= KEPT_PACK_IN_CORE_OPEN;
 
 		/*
 		 * If the object is in a pack that we want to ignore, *and* we
@@ -1657,6 +1670,8 @@ static int want_found_object(const struct object_id *oid, int exclude,
 				return 0;
 			if (ignore_packed_keep_in_core && p->pack_keep_in_core)
 				return 0;
+			if (ignore_packed_keep_in_core_open && p->pack_keep_in_core_open)
+				return 0;
 			if (has_object_kept_pack(p->repo, oid, flags))
 				return 0;
 		} else {
@@ -2236,7 +2251,7 @@ static void check_object(struct object_entry *entry, uint32_t object_index)
 		off_t ofs;
 		unsigned char *buf, c;
 		enum object_type type;
-		unsigned long in_pack_size;
+		size_t in_pack_size;
 
 		buf = use_pack(p, &w_curs, entry->in_pack_offset, &avail);
 
@@ -2263,7 +2278,7 @@ static void check_object(struct object_entry *entry, uint32_t object_index)
 		default:
 			/* Not a delta hence we've already got all we need. */
 			oe_set_type(entry, entry->in_pack_type);
-			SET_SIZE(entry, in_pack_size);
+			SET_SIZE(entry, cast_size_t_to_ulong(in_pack_size));
 			entry->in_pack_header_size = used;
 			if (oe_type(entry) < OBJ_COMMIT || oe_type(entry) > OBJ_BLOB)
 				goto give_up;
@@ -2317,8 +2332,8 @@ static void check_object(struct object_entry *entry, uint32_t object_index)
 		if (have_base &&
 		    can_reuse_delta(&base_ref, entry, &base_entry)) {
 			oe_set_type(entry, entry->in_pack_type);
-			SET_SIZE(entry, in_pack_size); /* delta size */
-			SET_DELTA_SIZE(entry, in_pack_size);
+			SET_SIZE(entry, cast_size_t_to_ulong(in_pack_size)); /* delta size */
+			SET_DELTA_SIZE(entry, cast_size_t_to_ulong(in_pack_size));
 
 			if (base_entry) {
 				SET_DELTA(entry, base_entry);
@@ -2727,16 +2742,18 @@ unsigned long oe_get_size_slow(struct packing_data *pack,
 	struct pack_window *w_curs;
 	unsigned char *buf;
 	enum object_type type;
-	unsigned long used, avail, size;
+	unsigned long used, avail;
+	size_t size;
 
 	if (e->type_ != OBJ_OFS_DELTA && e->type_ != OBJ_REF_DELTA) {
+		unsigned long sz;
 		packing_data_lock(&to_pack);
 		if (odb_read_object_info(the_repository->objects,
-					 &e->idx.oid, &size) < 0)
+					 &e->idx.oid, &sz) < 0)
 			die(_("unable to get size of %s"),
 			    oid_to_hex(&e->idx.oid));
 		packing_data_unlock(&to_pack);
-		return size;
+		return sz;
 	}
 
 	p = oe_in_pack(pack, e);
@@ -2753,7 +2770,7 @@ unsigned long oe_get_size_slow(struct packing_data *pack,
 
 	unuse_pack(&w_curs);
 	packing_data_unlock(&to_pack);
-	return size;
+	return cast_size_t_to_ulong(size);
 }
 
 static int try_delta(struct unpacked *trg, struct unpacked *src,
@@ -3756,6 +3773,7 @@ static int add_object_entry_from_pack(const struct object_id *oid,
 				      void *_data)
 {
 	off_t ofs;
+	struct object_info oi = OBJECT_INFO_INIT;
 	enum object_type type = OBJ_NONE;
 
 	display_progress(progress_state, ++nr_seen);
@@ -3763,29 +3781,34 @@ static int add_object_entry_from_pack(const struct object_id *oid,
 	if (have_duplicate_entry(oid, 0))
 		return 0;
 
+	stdin_packs_found_nr++;
+
 	ofs = nth_packed_object_offset(p, pos);
+
+	oi.typep = &type;
+	if (packed_object_info(p, ofs, &oi) < 0) {
+		die(_("could not get type of object %s in pack %s"),
+		    oid_to_hex(oid), p->pack_name);
+	} else if (type == OBJ_COMMIT) {
+		struct rev_info *revs = _data;
+		/*
+		 * commits in included packs are used as starting points
+		 * for the subsequent revision walk
+		 *
+		 * Note that we do want to walk through commits that are
+		 * present in excluded-open ('!') packs to pick up any
+		 * objects reachable from them not present in the
+		 * excluded-closed ('^') packs.
+		 *
+		 * However, we'll only add those objects to the packing
+		 * list after checking `want_object_in_pack()` below.
+		 */
+		add_pending_oid(revs, NULL, oid, 0);
+	}
+
 	if (!want_object_in_pack(oid, 0, &p, &ofs))
 		return 0;
 
-	if (p) {
-		struct object_info oi = OBJECT_INFO_INIT;
-
-		oi.typep = &type;
-		if (packed_object_info(p, ofs, &oi) < 0) {
-			die(_("could not get type of object %s in pack %s"),
-			    oid_to_hex(oid), p->pack_name);
-		} else if (type == OBJ_COMMIT) {
-			struct rev_info *revs = _data;
-			/*
-			 * commits in included packs are used as starting points for the
-			 * subsequent revision walk
-			 */
-			add_pending_oid(revs, NULL, oid, 0);
-		}
-
-		stdin_packs_found_nr++;
-	}
-
 	create_object_entry(oid, type, 0, 0, 0, p, ofs);
 
 	return 0;
@@ -3835,87 +3858,78 @@ static void show_commit_pack_hint(struct commit *commit, void *data)
 
 }
 
+/*
+ * stdin_pack_info_kind specifies how a pack specified over stdin
+ * should be treated when pack-objects is invoked with --stdin-packs.
+ *
+ *  - STDIN_PACK_INCLUDE: objects in any packs with this flag bit set
+ *    should be included in the output pack, unless they appear in an
+ *    excluded pack.
+ *
+ *  - STDIN_PACK_EXCLUDE_CLOSED: objects in any packs with this flag
+ *    bit set should be excluded from the output pack.
+ *
+ *  - STDIN_PACK_EXCLUDE_OPEN: objects in any packs with this flag
+ *    bit set should be excluded from the output pack, but are not
+ *    guaranteed to be closed under reachability.
+ *
+ * Objects in packs whose 'kind' bits include STDIN_PACK_INCLUDE or
+ * STDIN_PACK_EXCLUDE_OPEN are used as traversal tips when invoked
+ * with --stdin-packs=follow.
+ */
+enum stdin_pack_info_kind {
+	STDIN_PACK_INCLUDE = (1<<0),
+	STDIN_PACK_EXCLUDE_CLOSED = (1<<1),
+	STDIN_PACK_EXCLUDE_OPEN = (1<<2),
+};
+
+struct stdin_pack_info {
+	struct packed_git *p;
+	enum stdin_pack_info_kind kind;
+};
+
 static int pack_mtime_cmp(const void *_a, const void *_b)
 {
-	struct packed_git *a = ((const struct string_list_item*)_a)->util;
-	struct packed_git *b = ((const struct string_list_item*)_b)->util;
+	struct stdin_pack_info *a = ((const struct string_list_item*)_a)->util;
+	struct stdin_pack_info *b = ((const struct string_list_item*)_b)->util;
 
 	/*
 	 * order packs by descending mtime so that objects are laid out
 	 * roughly as newest-to-oldest
 	 */
-	if (a->mtime < b->mtime)
+	if (a->p->mtime < b->p->mtime)
 		return 1;
-	else if (b->mtime < a->mtime)
+	else if (b->p->mtime < a->p->mtime)
 		return -1;
 	else
 		return 0;
 }
 
-static void read_packs_list_from_stdin(struct rev_info *revs)
+static int stdin_packs_include_check_obj(struct object *obj, void *data UNUSED)
 {
-	struct strbuf buf = STRBUF_INIT;
-	struct string_list include_packs = STRING_LIST_INIT_DUP;
-	struct string_list exclude_packs = STRING_LIST_INIT_DUP;
-	struct string_list_item *item = NULL;
-	struct packed_git *p;
+	return !has_object_kept_pack(to_pack.repo, &obj->oid,
+				     KEPT_PACK_IN_CORE);
+}
 
-	while (strbuf_getline(&buf, stdin) != EOF) {
-		if (!buf.len)
-			continue;
+static int stdin_packs_include_check(struct commit *commit, void *data)
+{
+	return stdin_packs_include_check_obj((struct object *)commit, data);
+}
 
-		if (*buf.buf == '^')
-			string_list_append(&exclude_packs, buf.buf + 1);
-		else
-			string_list_append(&include_packs, buf.buf);
+static void stdin_packs_add_pack_entries(struct strmap *packs,
+					 struct rev_info *revs)
+{
+	struct string_list keys = STRING_LIST_INIT_NODUP;
+	struct string_list_item *item;
+	struct hashmap_iter iter;
+	struct strmap_entry *entry;
 
-		strbuf_reset(&buf);
-	}
+	strmap_for_each_entry(packs, &iter, entry) {
+		struct stdin_pack_info *info = entry->value;
+		if (!info->p)
+			die(_("could not find pack '%s'"), entry->key);
 
-	string_list_sort_u(&include_packs, 0);
-	string_list_sort_u(&exclude_packs, 0);
-
-	repo_for_each_pack(the_repository, p) {
-		const char *pack_name = pack_basename(p);
-
-		if ((item = string_list_lookup(&include_packs, pack_name))) {
-			if (exclude_promisor_objects && p->pack_promisor)
-				die(_("packfile %s is a promisor but --exclude-promisor-objects was given"), p->pack_name);
-			item->util = p;
-		}
-		if ((item = string_list_lookup(&exclude_packs, pack_name)))
-			item->util = p;
-	}
-
-	/*
-	 * Arguments we got on stdin may not even be packs. First
-	 * check that to avoid segfaulting later on in
-	 * e.g. pack_mtime_cmp(), excluded packs are handled below.
-	 *
-	 * Since we first parsed our STDIN and then sorted the input
-	 * lines the pack we error on will be whatever line happens to
-	 * sort first. This is lazy, it's enough that we report one
-	 * bad case here, we don't need to report the first/last one,
-	 * or all of them.
-	 */
-	for_each_string_list_item(item, &include_packs) {
-		struct packed_git *p = item->util;
-		if (!p)
-			die(_("could not find pack '%s'"), item->string);
-		if (!is_pack_valid(p))
-			die(_("packfile %s cannot be accessed"), p->pack_name);
-	}
-
-	/*
-	 * Then, handle all of the excluded packs, marking them as
-	 * kept in-core so that later calls to add_object_entry()
-	 * discards any objects that are also found in excluded packs.
-	 */
-	for_each_string_list_item(item, &exclude_packs) {
-		struct packed_git *p = item->util;
-		if (!p)
-			die(_("could not find pack '%s'"), item->string);
-		p->pack_keep_in_core = 1;
+		string_list_append(&keys, entry->key)->util = info;
 	}
 
 	/*
@@ -3923,19 +3937,118 @@ static void read_packs_list_from_stdin(struct rev_info *revs)
 	 * string_list_item's ->util pointer, which string_list_sort() does not
 	 * provide.
 	 */
-	QSORT(include_packs.items, include_packs.nr, pack_mtime_cmp);
+	QSORT(keys.items, keys.nr, pack_mtime_cmp);
 
-	for_each_string_list_item(item, &include_packs) {
-		struct packed_git *p = item->util;
-		for_each_object_in_pack(p,
-					add_object_entry_from_pack,
-					revs,
-					ODB_FOR_EACH_OBJECT_PACK_ORDER);
+	for_each_string_list_item(item, &keys) {
+		struct stdin_pack_info *info = item->util;
+
+		if (info->kind & STDIN_PACK_EXCLUDE_OPEN) {
+			/*
+			 * When open-excluded packs ("!") are present, stop
+			 * the parent walk at closed-excluded ("^") packs.
+			 * Objects behind a "^" boundary are guaranteed to
+			 * have closure and should not be rescued.
+			 */
+			revs->include_check = stdin_packs_include_check;
+			revs->include_check_obj = stdin_packs_include_check_obj;
+		}
+
+		if ((info->kind & STDIN_PACK_INCLUDE) ||
+		    (info->kind & STDIN_PACK_EXCLUDE_OPEN))
+			for_each_object_in_pack(info->p,
+						add_object_entry_from_pack,
+						revs,
+						ODB_FOR_EACH_OBJECT_PACK_ORDER);
 	}
 
+	string_list_clear(&keys, 0);
+}
+
+static void stdin_packs_read_input(struct rev_info *revs,
+				   enum stdin_packs_mode mode)
+{
+	struct strbuf buf = STRBUF_INIT;
+	struct strmap packs = STRMAP_INIT;
+	struct packed_git *p;
+
+	while (strbuf_getline(&buf, stdin) != EOF) {
+		struct stdin_pack_info *info;
+		enum stdin_pack_info_kind kind = STDIN_PACK_INCLUDE;
+		const char *key = buf.buf;
+
+		if (!*key)
+			continue;
+		else if (*key == '^')
+			kind = STDIN_PACK_EXCLUDE_CLOSED;
+		else if (*key == '!' && mode == STDIN_PACKS_MODE_FOLLOW)
+			kind = STDIN_PACK_EXCLUDE_OPEN;
+
+		if (kind != STDIN_PACK_INCLUDE)
+			key++;
+
+		info = strmap_get(&packs, key);
+		if (!info) {
+			CALLOC_ARRAY(info, 1);
+			strmap_put(&packs, key, info);
+		}
+
+		info->kind |= kind;
+
+		strbuf_reset(&buf);
+	}
+
+	repo_for_each_pack(the_repository, p) {
+		struct stdin_pack_info *info;
+
+		info = strmap_get(&packs, pack_basename(p));
+		if (!info)
+			continue;
+
+		if (info->kind & STDIN_PACK_INCLUDE) {
+			if (exclude_promisor_objects && p->pack_promisor)
+				die(_("packfile %s is a promisor but --exclude-promisor-objects was given"), p->pack_name);
+
+			/*
+			 * Arguments we got on stdin may not even be
+			 * packs. First check that to avoid segfaulting
+			 * later on in e.g.  pack_mtime_cmp(), excluded
+			 * packs are handled below.
+			 */
+			if (!is_pack_valid(p))
+				die(_("packfile %s cannot be accessed"), p->pack_name);
+		}
+
+		if (info->kind & STDIN_PACK_EXCLUDE_CLOSED) {
+			/*
+			 * Marking excluded packs as kept in-core so
+			 * that later calls to add_object_entry()
+			 * discards any objects that are also found in
+			 * excluded packs.
+			 */
+			p->pack_keep_in_core = 1;
+		}
+
+		if (info->kind & STDIN_PACK_EXCLUDE_OPEN) {
+			/*
+			 * Marking excluded open packs as kept in-core
+			 * (open) for the same reason as we marked
+			 * exclude closed packs as kept in-core.
+			 *
+			 * Use a separate flag here to ensure we don't
+			 * halt our traversal at these packs, since they
+			 * are not guaranteed to have closure.
+			 *
+			 */
+			p->pack_keep_in_core_open = 1;
+		}
+
+		info->p = p;
+	}
+
+	stdin_packs_add_pack_entries(&packs, revs);
+
 	strbuf_release(&buf);
-	string_list_clear(&include_packs, 0);
-	string_list_clear(&exclude_packs, 0);
+	strmap_clear(&packs, 1);
 }
 
 static void add_unreachable_loose_objects(struct rev_info *revs);
@@ -3972,7 +4085,15 @@ static void read_stdin_packs(enum stdin_packs_mode mode, int rev_list_unpacked)
 
 	/* avoids adding objects in excluded packs */
 	ignore_packed_keep_in_core = 1;
-	read_packs_list_from_stdin(&revs);
+	if (mode == STDIN_PACKS_MODE_FOLLOW) {
+		/*
+		 * In '--stdin-packs=follow' mode, additionally ignore
+		 * objects in excluded-open packs to prevent them from
+		 * appearing in the resulting pack.
+		 */
+		ignore_packed_keep_in_core_open = 1;
+	}
+	stdin_packs_read_input(&revs, mode);
 	if (rev_list_unpacked)
 		add_unreachable_loose_objects(&revs);
 
@@ -3983,6 +4104,8 @@ static void read_stdin_packs(enum stdin_packs_mode mode, int rev_list_unpacked)
 			     show_object_pack_hint,
 			     &mode);
 
+	release_revisions(&revs);
+
 	trace2_data_intmax("pack-objects", the_repository, "stdin_packs_found",
 			   stdin_packs_found_nr);
 	trace2_data_intmax("pack-objects", the_repository, "stdin_packs_hints",
@@ -4359,6 +4482,12 @@ static void add_objects_in_unpacked_packs(void)
 {
 	struct odb_source *source;
 	time_t mtime;
+	struct odb_for_each_object_options opts = {
+		.flags = ODB_FOR_EACH_OBJECT_PACK_ORDER |
+			 ODB_FOR_EACH_OBJECT_LOCAL_ONLY |
+			 ODB_FOR_EACH_OBJECT_SKIP_IN_CORE_KEPT_PACKS |
+			 ODB_FOR_EACH_OBJECT_SKIP_ON_DISK_KEPT_PACKS,
+	};
 	struct object_info oi = {
 		.mtimep = &mtime,
 	};
@@ -4371,11 +4500,7 @@ static void add_objects_in_unpacked_packs(void)
 			continue;
 
 		if (packfile_store_for_each_object(files->packed, &oi,
-						   add_object_in_unpacked_pack, NULL,
-						   ODB_FOR_EACH_OBJECT_PACK_ORDER |
-						   ODB_FOR_EACH_OBJECT_LOCAL_ONLY |
-						   ODB_FOR_EACH_OBJECT_SKIP_IN_CORE_KEPT_PACKS |
-						   ODB_FOR_EACH_OBJECT_SKIP_ON_DISK_KEPT_PACKS))
+						   add_object_in_unpacked_pack, NULL, &opts))
 			die(_("cannot open pack index"));
 	}
 }
@@ -4639,7 +4764,7 @@ static int add_objects_by_path(const char *path,
 	return 0;
 }
 
-static void get_object_list_path_walk(struct rev_info *revs)
+static int get_object_list_path_walk(struct rev_info *revs)
 {
 	struct path_walk_info info = PATH_WALK_INFO_INIT;
 	unsigned int processed = 0;
@@ -4662,8 +4787,9 @@ static void get_object_list_path_walk(struct rev_info *revs)
 	result = walk_objects_by_path(&info);
 	trace2_region_leave("pack-objects", "path-walk", revs->repo);
 
-	if (result)
-		die(_("failed to pack objects via path-walk"));
+	path_walk_info_clear(&info);
+
+	return result;
 }
 
 static void get_object_list(struct rev_info *revs, struct strvec *argv)
@@ -4726,8 +4852,13 @@ static void get_object_list(struct rev_info *revs, struct strvec *argv)
 		fn_show_object = show_object;
 
 	if (path_walk) {
-		get_object_list_path_walk(revs);
-	} else {
+		if (get_object_list_path_walk(revs)) {
+			warning(_("failed to pack objects via path-walk"));
+			path_walk = 0;
+		}
+	}
+
+	if (!path_walk) {
 		if (prepare_revision_walk(revs))
 			die(_("revision walk setup failed"));
 		mark_edges_uninteresting(revs, show_edge, sparse);
@@ -5062,7 +5193,7 @@ int cmd_pack_objects(int argc,
 
 	if (path_walk) {
 		const char *option = NULL;
-		if (filter_options.choice)
+		if (!path_walk_filter_compatible(&filter_options))
 			option = "--filter";
 		else if (use_delta_islands)
 			option = "--delta-islands";
@@ -5075,10 +5206,7 @@ int cmd_pack_objects(int argc,
 	}
 	if (path_walk) {
 		strvec_push(&rp, "--boundary");
-		 /*
-		  * We must disable the bitmaps because we are removing
-		  * the --objects / --objects-edge[-aggressive] options.
-		  */
+		strvec_push(&rp, "--objects");
 		use_bitmap_index = 0;
 	} else if (thin) {
 		use_internal_rev_list = 1;
diff --git a/builtin/pull.c b/builtin/pull.c
index 7e67fdc..d49b091 100644
--- a/builtin/pull.c
+++ b/builtin/pull.c
@@ -996,9 +996,13 @@ int cmd_pull(int argc,
 		OPT_PASSTHRU('6',  "ipv6", &opt_ipv6, NULL,
 			N_("use IPv6 addresses only"),
 			PARSE_OPT_NOARG),
-		OPT_PASSTHRU_ARGV(0, "negotiation-tip", &opt_fetch, N_("revision"),
+		OPT_PASSTHRU_ARGV(0, "negotiation-restrict", &opt_fetch, N_("revision"),
 			N_("report that we have only objects reachable from this object"),
 			0),
+		OPT_ALIAS(0, "negotiation-tip", "negotiation-restrict"),
+		OPT_PASSTHRU_ARGV(0, "negotiation-include", &opt_fetch, N_("revision"),
+			N_("ensure this ref is always sent as a negotiation have"),
+			0),
 		OPT_BOOL(0, "show-forced-updates", &opt_show_forced_updates,
 			 N_("check for forced-updates on all updated branches")),
 		OPT_PASSTHRU(0, "set-upstream", &set_upstream, NULL,
diff --git a/builtin/read-tree.c b/builtin/read-tree.c
index 460b21e..999a82e 100644
--- a/builtin/read-tree.c
+++ b/builtin/read-tree.c
@@ -229,7 +229,7 @@ int cmd_read_tree(int argc,
 		opts.preserve_ignored = 0;
 	/* otherwise, opts.preserve_ignored is irrelevant */
 	if (opts.merge && !opts.index_only)
-		setup_work_tree();
+		setup_work_tree(the_repository);
 
 	if (opts.skip_sparse_checkout)
 		ensure_full_index(the_repository->index);
diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c
index cb3656a..19eb6a1 100644
--- a/builtin/receive-pack.c
+++ b/builtin/receive-pack.c
@@ -1029,8 +1029,8 @@ static int read_proc_receive_report(struct packet_reader *reader,
 
 	for (;;) {
 		struct object_id old_oid, new_oid;
-		const char *head;
-		const char *refname;
+		char *head;
+		char *refname;
 		char *p;
 		enum packet_read_status status;
 
@@ -1054,7 +1054,8 @@ static int read_proc_receive_report(struct packet_reader *reader,
 		}
 		*p++ = '\0';
 		if (!strcmp(head, "option")) {
-			const char *key, *val;
+			char *key;
+			const char *val;
 
 			if (!hint || !(report || new_report)) {
 				if (!once++)
@@ -1384,32 +1385,16 @@ static int update_shallow_ref(struct command *cmd, struct shallow_info *si)
 	return 0;
 }
 
-/*
- * NEEDSWORK: we should consolidate various implementations of "are we
- * on an unborn branch?" test into one, and make the unified one more
- * robust. !get_sha1() based check used here and elsewhere would not
- * allow us to tell an unborn branch from corrupt ref, for example.
- * For the purpose of fixing "deploy-to-update does not work when
- * pushing into an empty repository" issue, this should suffice for
- * now.
- */
-static int head_has_history(void)
-{
-	struct object_id oid;
-
-	return !repo_get_oid(the_repository, "HEAD", &oid);
-}
-
 static const char *push_to_deploy(unsigned char *sha1,
 				  struct strvec *env,
-				  const char *work_tree)
+				  const struct worktree *worktree)
 {
 	struct child_process child = CHILD_PROCESS_INIT;
 
 	strvec_pushl(&child.args, "update-index", "-q", "--ignore-submodules",
 		     "--refresh", NULL);
 	strvec_pushv(&child.env, env->v);
-	child.dir = work_tree;
+	child.dir = worktree->path;
 	child.no_stdin = 1;
 	child.stdout_to_stderr = 1;
 	child.git_cmd = 1;
@@ -1421,7 +1406,7 @@ static const char *push_to_deploy(unsigned char *sha1,
 	strvec_pushl(&child.args, "diff-files", "--quiet",
 		     "--ignore-submodules", "--", NULL);
 	strvec_pushv(&child.env, env->v);
-	child.dir = work_tree;
+	child.dir = worktree->path;
 	child.no_stdin = 1;
 	child.stdout_to_stderr = 1;
 	child.git_cmd = 1;
@@ -1431,9 +1416,16 @@ static const char *push_to_deploy(unsigned char *sha1,
 	child_process_init(&child);
 	strvec_pushl(&child.args, "diff-index", "--quiet", "--cached",
 		     "--ignore-submodules",
-		     /* diff-index with either HEAD or an empty tree */
-		     head_has_history() ? "HEAD" : empty_tree_oid_hex(the_repository->hash_algo),
-		     "--", NULL);
+		     /*
+		      * diff-index with either HEAD or an empty tree
+		      *
+		      * NEEDSWORK: is_null_oid() cannot know whether it's an
+		      * unborn HEAD or a corrupt ref. It works for now because
+		      * it's only needed to know if we are comparing HEAD or an
+		      * empty tree.
+		      */
+		     !is_null_oid(&worktree->head_oid) ? "HEAD" :
+		     empty_tree_oid_hex(the_repository->hash_algo), "--", NULL);
 	strvec_pushv(&child.env, env->v);
 	child.no_stdin = 1;
 	child.no_stdout = 1;
@@ -1446,7 +1438,7 @@ static const char *push_to_deploy(unsigned char *sha1,
 	strvec_pushl(&child.args, "read-tree", "-u", "-m", hash_to_hex(sha1),
 		     NULL);
 	strvec_pushv(&child.env, env->v);
-	child.dir = work_tree;
+	child.dir = worktree->path;
 	child.no_stdin = 1;
 	child.no_stdout = 1;
 	child.stdout_to_stderr = 0;
@@ -1464,11 +1456,12 @@ static const char *push_to_checkout(unsigned char *hash,
 				    struct strvec *env,
 				    const char *work_tree)
 {
-	struct run_hooks_opt opt = RUN_HOOKS_OPT_INIT;
+	struct run_hooks_opt opt = RUN_HOOKS_OPT_INIT_FORCE_SERIAL;
+
 	opt.invoked_hook = invoked_hook;
 
-	strvec_pushf(env, "GIT_WORK_TREE=%s", absolute_path(work_tree));
 	strvec_pushv(&opt.env, env->v);
+	strvec_pushf(&opt.env, "GIT_WORK_TREE=%s", absolute_path(work_tree));
 	strvec_push(&opt.args, hash_to_hex(hash));
 	if (run_hooks_opt(the_repository, push_to_checkout_hook, &opt))
 		return "push-to-checkout hook declined";
@@ -1494,7 +1487,7 @@ static const char *update_worktree(unsigned char *sha1, const struct worktree *w
 
 	retval = push_to_checkout(sha1, &invoked_hook, &env, worktree->path);
 	if (!invoked_hook)
-		retval = push_to_deploy(sha1, &env, worktree->path);
+		retval = push_to_deploy(sha1, &env, worktree);
 
 	strvec_clear(&env);
 	free(git_dir);
@@ -1550,7 +1543,7 @@ static const char *update(struct command *cmd, struct shallow_info *si)
 
 	if (!is_null_oid(new_oid) &&
 	    !odb_has_object(the_repository->objects, new_oid,
-			    HAS_OBJECT_RECHECK_PACKED | HAS_OBJECT_FETCH_PROMISOR)) {
+			    ODB_HAS_OBJECT_RECHECK_PACKED | ODB_HAS_OBJECT_FETCH_PROMISOR)) {
 		error("unpack should have generated %s, "
 		      "but I can't find it!", oid_to_hex(new_oid));
 		ret = "bad pack";
@@ -1649,8 +1642,8 @@ static const char *update(struct command *cmd, struct shallow_info *si)
 			ret = NULL; /* good */
 		}
 		strbuf_release(&err);
-	}
-	else {
+	} else {
+		enum ref_transaction_error tx_err;
 		struct strbuf err = STRBUF_INIT;
 		if (shallow_update && si->shallow_ref[cmd->index] &&
 		    update_shallow_ref(cmd, si)) {
@@ -1658,14 +1651,18 @@ static const char *update(struct command *cmd, struct shallow_info *si)
 			goto out;
 		}
 
-		if (ref_transaction_update(transaction,
-					   namespaced_name,
-					   new_oid, old_oid,
-					   NULL, NULL,
-					   0, "push",
-					   &err)) {
+		tx_err = ref_transaction_update(transaction,
+						  namespaced_name,
+						  new_oid, old_oid,
+						  NULL, NULL,
+						  0, "push",
+						  &err);
+		if (tx_err) {
 			rp_error("%s", err.buf);
-			ret = "failed to update ref";
+			if (tx_err == REF_TRANSACTION_ERROR_GENERIC)
+				ret = "failed to update ref";
+			else
+				ret = ref_transaction_error_msg(tx_err);
 		} else {
 			ret = NULL; /* good */
 		}
@@ -2650,7 +2647,7 @@ int cmd_receive_pack(int argc,
 
 	setup_path();
 
-	if (!enter_repo(service_dir, 0))
+	if (!enter_repo(the_repository, service_dir, 0))
 		die("'%s' does not appear to be a git repository", service_dir);
 
 	repo_config(the_repository, receive_pack_config, NULL);
diff --git a/builtin/refs.c b/builtin/refs.c
index 3064f88..e3125bc 100644
--- a/builtin/refs.c
+++ b/builtin/refs.c
@@ -78,9 +78,9 @@ static int cmd_refs_migrate(int argc, const char **argv, const char *prefix,
 }
 
 static int cmd_refs_verify(int argc, const char **argv, const char *prefix,
-			   struct repository *repo UNUSED)
+			   struct repository *repo)
 {
-	struct fsck_options fsck_refs_options = FSCK_REFS_OPTIONS_DEFAULT;
+	struct fsck_options fsck_refs_options;
 	struct worktree **worktrees;
 	const char * const verify_usage[] = {
 		REFS_VERIFY_USAGE,
@@ -93,6 +93,8 @@ static int cmd_refs_verify(int argc, const char **argv, const char *prefix,
 	};
 	int ret = 0;
 
+	fsck_options_init(&fsck_refs_options, repo, FSCK_OPTIONS_REFS);
+
 	argc = parse_options(argc, argv, prefix, options, verify_usage, 0);
 	if (argc)
 		usage(_("'git refs verify' takes no arguments"));
diff --git a/builtin/remote.c b/builtin/remote.c
index 0fddaa1..de989ea 100644
--- a/builtin/remote.c
+++ b/builtin/remote.c
@@ -473,7 +473,7 @@ static int get_push_ref_states(const struct ref *remote_refs,
 		else if (is_null_oid(&ref->old_oid))
 			info->status = PUSH_STATUS_CREATE;
 		else if (odb_has_object(the_repository->objects, &ref->old_oid,
-					HAS_OBJECT_RECHECK_PACKED | HAS_OBJECT_FETCH_PROMISOR) &&
+					ODB_HAS_OBJECT_RECHECK_PACKED | ODB_HAS_OBJECT_FETCH_PROMISOR) &&
 			 ref_newer(&ref->new_oid, &ref->old_oid))
 			info->status = PUSH_STATUS_FASTFORWARD;
 		else
diff --git a/builtin/repack.c b/builtin/repack.c
index f6bb04b..1524a9c 100644
--- a/builtin/repack.c
+++ b/builtin/repack.c
@@ -33,7 +33,7 @@ static int midx_must_contain_cruft = 1;
 static const char *const git_repack_usage[] = {
 	N_("git repack [-a] [-A] [-d] [-f] [-F] [-l] [-n] [-q] [-b] [-m]\n"
 	   "[--window=<n>] [--depth=<n>] [--threads=<n>] [--keep-pack=<pack-name>]\n"
-	   "[--write-midx] [--name-hash-version=<n>] [--path-walk]"),
+	   "[--write-midx[=<mode>]] [--name-hash-version=<n>] [--path-walk]"),
 	NULL
 };
 
@@ -42,9 +42,14 @@ static const char incremental_bitmap_conflict_error[] = N_(
 "--no-write-bitmap-index or disable the pack.writeBitmaps configuration."
 );
 
+#define DEFAULT_MIDX_SPLIT_FACTOR 2
+#define DEFAULT_MIDX_NEW_LAYER_THRESHOLD 8
+
 struct repack_config_ctx {
 	struct pack_objects_args *po_args;
 	struct pack_objects_args *cruft_po_args;
+	int midx_split_factor;
+	int midx_new_layer_threshold;
 };
 
 static int repack_config(const char *var, const char *value,
@@ -94,9 +99,39 @@ static int repack_config(const char *var, const char *value,
 		midx_must_contain_cruft = git_config_bool(var, value);
 		return 0;
 	}
+	if (!strcmp(var, "repack.midxsplitfactor")) {
+		repack_ctx->midx_split_factor = git_config_int(var, value,
+							       ctx->kvi);
+		return 0;
+	}
+	if (!strcmp(var, "repack.midxnewlayerthreshold")) {
+		repack_ctx->midx_new_layer_threshold = git_config_int(var, value,
+								      ctx->kvi);
+		return 0;
+	}
 	return git_default_config(var, value, ctx, cb);
 }
 
+static int option_parse_write_midx(const struct option *opt, const char *arg,
+				   int unset)
+{
+	enum repack_write_midx_mode *cfg = opt->value;
+
+	if (unset) {
+		*cfg = REPACK_WRITE_MIDX_NONE;
+		return 0;
+	}
+
+	if (!arg || !*arg)
+		*cfg = REPACK_WRITE_MIDX_DEFAULT;
+	else if (!strcmp(arg, "incremental"))
+		*cfg = REPACK_WRITE_MIDX_INCREMENTAL;
+	else
+		return error(_("unknown value for %s: %s"), opt->long_name, arg);
+
+	return 0;
+}
+
 int cmd_repack(int argc,
 	       const char **argv,
 	       const char *prefix,
@@ -119,7 +154,7 @@ int cmd_repack(int argc,
 	struct string_list keep_pack_list = STRING_LIST_INIT_NODUP;
 	struct pack_objects_args po_args = PACK_OBJECTS_ARGS_INIT;
 	struct pack_objects_args cruft_po_args = PACK_OBJECTS_ARGS_INIT;
-	int write_midx = 0;
+	enum repack_write_midx_mode write_midx = REPACK_WRITE_MIDX_NONE;
 	const char *cruft_expiration = NULL;
 	const char *expire_to = NULL;
 	const char *filter_to = NULL;
@@ -185,8 +220,14 @@ int cmd_repack(int argc,
 				N_("do not repack this pack")),
 		OPT_INTEGER('g', "geometric", &geometry.split_factor,
 			    N_("find a geometric progression with factor <N>")),
-		OPT_BOOL('m', "write-midx", &write_midx,
-			   N_("write a multi-pack index of the resulting packs")),
+		OPT_CALLBACK_F(0, "write-midx", &write_midx,
+			   N_("mode"),
+			   N_("write a multi-pack index of the resulting packs"),
+			   PARSE_OPT_OPTARG, option_parse_write_midx),
+		OPT_SET_INT_F('m', NULL, &write_midx,
+			   N_("write a multi-pack index of the resulting packs"),
+			   REPACK_WRITE_MIDX_DEFAULT,
+			   PARSE_OPT_HIDDEN),
 		OPT_STRING(0, "expire-to", &expire_to, N_("dir"),
 			   N_("pack prefix to store a pack containing pruned objects")),
 		OPT_STRING(0, "filter-to", &filter_to, N_("dir"),
@@ -199,6 +240,8 @@ int cmd_repack(int argc,
 	memset(&config_ctx, 0, sizeof(config_ctx));
 	config_ctx.po_args = &po_args;
 	config_ctx.cruft_po_args = &cruft_po_args;
+	config_ctx.midx_split_factor = DEFAULT_MIDX_SPLIT_FACTOR;
+	config_ctx.midx_new_layer_threshold = DEFAULT_MIDX_NEW_LAYER_THRESHOLD;
 
 	repo_config(repo, repack_config, &config_ctx);
 
@@ -221,14 +264,16 @@ int cmd_repack(int argc,
 		pack_everything |= ALL_INTO_ONE;
 
 	if (write_bitmaps < 0) {
-		if (!write_midx &&
+		if (write_midx == REPACK_WRITE_MIDX_NONE &&
 		    (!(pack_everything & ALL_INTO_ONE) || !is_bare_repository()))
 			write_bitmaps = 0;
 	}
 	if (po_args.pack_kept_objects < 0)
-		po_args.pack_kept_objects = write_bitmaps > 0 && !write_midx;
+		po_args.pack_kept_objects = write_bitmaps > 0 &&
+			write_midx == REPACK_WRITE_MIDX_NONE;
 
-	if (write_bitmaps && !(pack_everything & ALL_INTO_ONE) && !write_midx)
+	if (write_bitmaps && !(pack_everything & ALL_INTO_ONE) &&
+	    write_midx == REPACK_WRITE_MIDX_NONE)
 		die(_(incremental_bitmap_conflict_error));
 
 	if (write_bitmaps && po_args.local &&
@@ -244,7 +289,14 @@ int cmd_repack(int argc,
 		write_bitmaps = 0;
 	}
 
-	if (write_midx && write_bitmaps) {
+	if (config_ctx.midx_split_factor < 2)
+		die(_("invalid value for %s: %d"), "--midx-split-factor",
+		    config_ctx.midx_split_factor);
+	if (config_ctx.midx_new_layer_threshold < 1)
+		die(_("invalid value for %s: %d"), "--midx-new-layer-threshold",
+		    config_ctx.midx_new_layer_threshold);
+
+	if (write_midx != REPACK_WRITE_MIDX_NONE && write_bitmaps) {
 		struct strbuf path = STRBUF_INIT;
 
 		strbuf_addf(&path, "%s/%s_XXXXXX",
@@ -267,6 +319,10 @@ int cmd_repack(int argc,
 	if (geometry.split_factor) {
 		if (pack_everything)
 			die(_("options '%s' and '%s' cannot be used together"), "--geometric", "-A/-a");
+		if (write_midx == REPACK_WRITE_MIDX_INCREMENTAL) {
+			geometry.midx_layer_threshold = config_ctx.midx_new_layer_threshold;
+			geometry.midx_layer_threshold_set = true;
+		}
 		pack_geometry_init(&geometry, &existing, &po_args);
 		pack_geometry_split(&geometry);
 	}
@@ -297,7 +353,7 @@ int cmd_repack(int argc,
 	}
 	if (repo_has_promisor_remote(repo))
 		strvec_push(&cmd.args, "--exclude-promisor-objects");
-	if (!write_midx) {
+	if (write_midx == REPACK_WRITE_MIDX_NONE) {
 		if (write_bitmaps > 0)
 			strvec_push(&cmd.args, "--write-bitmap-index");
 		else if (write_bitmaps < 0)
@@ -369,8 +425,23 @@ int cmd_repack(int argc,
 		 */
 		for (i = 0; i < geometry.split; i++)
 			fprintf(in, "%s\n", pack_basename(geometry.pack[i]));
-		for (i = geometry.split; i < geometry.pack_nr; i++)
-			fprintf(in, "^%s\n", pack_basename(geometry.pack[i]));
+		for (i = geometry.split; i < geometry.pack_nr; i++) {
+			const char *basename = pack_basename(geometry.pack[i]);
+			char marker = '^';
+
+			if (!midx_must_contain_cruft &&
+			    !string_list_has_string(&existing.midx_packs,
+						    basename)) {
+				/*
+				 * Assume non-MIDX'd packs are not
+				 * necessarily closed under
+				 * reachability.
+				 */
+				marker = '!';
+			}
+
+			fprintf(in, "%c%s\n", marker, basename);
+		}
 		fclose(in);
 	}
 
@@ -402,7 +473,7 @@ int cmd_repack(int argc,
 		 * midx_has_unknown_packs() will make the decision for
 		 * us.
 		 */
-		if (!get_multi_pack_index(repo->objects->sources))
+		if (!get_multi_pack_index(existing.source))
 			midx_must_contain_cruft = 1;
 	}
 
@@ -501,10 +572,13 @@ int cmd_repack(int argc,
 				       packtmp);
 	/* End of pack replacement. */
 
-	if (delete_redundant && pack_everything & ALL_INTO_ONE)
+	if (delete_redundant && pack_everything & ALL_INTO_ONE) {
+		if (write_midx == REPACK_WRITE_MIDX_INCREMENTAL)
+			existing_packs_retain_midx_packs(&existing);
 		existing_packs_mark_for_deletion(&existing, &names);
+	}
 
-	if (write_midx) {
+	if (write_midx != REPACK_WRITE_MIDX_NONE) {
 		struct repack_write_midx_opts opts = {
 			.existing = &existing,
 			.geometry = &geometry,
@@ -513,11 +587,13 @@ int cmd_repack(int argc,
 			.packdir = packdir,
 			.show_progress = show_progress,
 			.write_bitmaps = write_bitmaps > 0,
-			.midx_must_contain_cruft = midx_must_contain_cruft
+			.midx_must_contain_cruft = midx_must_contain_cruft,
+			.midx_split_factor = config_ctx.midx_split_factor,
+			.midx_new_layer_threshold = config_ctx.midx_new_layer_threshold,
+			.mode = write_midx,
 		};
 
-		ret = write_midx_included_packs(&opts);
-
+		ret = repack_write_midx(&opts);
 		if (ret)
 			goto cleanup;
 	}
@@ -526,11 +602,15 @@ int cmd_repack(int argc,
 
 	if (delete_redundant) {
 		int opts = 0;
-		existing_packs_remove_redundant(&existing, packdir);
+		bool wrote_incremental_midx = write_midx == REPACK_WRITE_MIDX_INCREMENTAL;
+
+		existing_packs_remove_redundant(&existing, packdir,
+						wrote_incremental_midx);
 
 		if (geometry.split_factor)
 			pack_geometry_remove_redundant(&geometry, &names,
-						       &existing, packdir);
+						       &existing, packdir,
+						       wrote_incremental_midx);
 		if (show_progress)
 			opts |= PRUNE_PACKED_VERBOSE;
 		prune_packed_objects(opts);
@@ -549,8 +629,7 @@ int cmd_repack(int argc,
 		unsigned flags = 0;
 		if (git_env_bool(GIT_TEST_MULTI_PACK_INDEX_WRITE_INCREMENTAL, 0))
 			flags |= MIDX_WRITE_INCREMENTAL;
-		write_midx_file(repo->objects->sources,
-				NULL, NULL, flags);
+		write_midx_file(existing.source, NULL, NULL, flags);
 	}
 
 cleanup:
diff --git a/builtin/replay.c b/builtin/replay.c
index a0879b0..39e3a86 100644
--- a/builtin/replay.c
+++ b/builtin/replay.c
@@ -84,25 +84,33 @@ int cmd_replay(int argc,
 
 	const char *const replay_usage[] = {
 		N_("(EXPERIMENTAL!) git replay "
-		   "([--contained] --onto <newbase> | --advance <branch> | --revert <branch>) "
-		   "[--ref-action[=<mode>]] <revision-range>"),
+		   "([--contained] --onto=<newbase> | --advance=<branch> | --revert=<branch>)\n"
+		   "[--ref=<ref>] [--ref-action=<mode>] <revision-range>"),
 		NULL
 	};
 	struct option replay_options[] = {
-		OPT_STRING(0, "advance", &opts.advance,
-			   N_("branch"),
-			   N_("make replay advance given branch")),
-		OPT_STRING(0, "onto", &opts.onto,
-			   N_("revision"),
-			   N_("replay onto given commit")),
 		OPT_BOOL(0, "contained", &opts.contained,
 			 N_("update all branches that point at commits in <revision-range>")),
-		OPT_STRING(0, "revert", &opts.revert,
-			   N_("branch"),
-			   N_("revert commits onto given branch")),
-		OPT_STRING(0, "ref-action", &ref_action,
-			   N_("mode"),
-			   N_("control ref update behavior (update|print)")),
+		OPT_STRING_F(0, "onto", &opts.onto,
+			     N_("revision"),
+			     N_("replay onto given commit"),
+			     PARSE_OPT_NONEG),
+		OPT_STRING_F(0, "advance", &opts.advance,
+			     N_("branch"),
+			     N_("make replay advance given branch"),
+			     PARSE_OPT_NONEG),
+		OPT_STRING_F(0, "revert", &opts.revert,
+			     N_("branch"),
+			     N_("revert commits onto given branch"),
+			     PARSE_OPT_NONEG),
+		OPT_STRING_F(0, "ref", &opts.ref,
+			     N_("branch"),
+			     N_("reference to update with result"),
+			     PARSE_OPT_NONEG),
+		OPT_STRING_F(0, "ref-action", &ref_action,
+			     N_("mode"),
+			     N_("control ref update behavior (update|print)"),
+			     PARSE_OPT_NONEG),
 		OPT_END()
 	};
 
@@ -122,6 +130,8 @@ int cmd_replay(int argc,
 				  opts.contained, "--contained");
 	die_for_incompatible_opt2(!!opts.revert, "--revert",
 				  opts.contained, "--contained");
+	die_for_incompatible_opt2(!!opts.ref, "--ref",
+				  !!opts.contained, "--contained");
 
 	/* Parse ref action mode from command line or config */
 	ref_mode = get_ref_action_mode(repo, ref_action);
diff --git a/builtin/reset.c b/builtin/reset.c
index 3590be5..3be6bd0 100644
--- a/builtin/reset.c
+++ b/builtin/reset.c
@@ -281,11 +281,11 @@ static void parse_args(struct pathspec *pathspec,
 			 * Ok, argv[0] looks like a commit/tree; it should not
 			 * be a filename.
 			 */
-			verify_non_filename(prefix, argv[0]);
+			verify_non_filename(the_repository, prefix, argv[0]);
 			rev = *argv++;
 		} else {
 			/* Otherwise we treat this as a filename */
-			verify_filename(prefix, argv[0], 1);
+			verify_filename(the_repository, prefix, argv[0], 1);
 		}
 	}
 
@@ -468,7 +468,7 @@ int cmd_reset(int argc,
 		trace2_cmd_mode(reset_type_names[reset_type]);
 
 	if (reset_type != SOFT && (reset_type != MIXED || repo_get_work_tree(the_repository)))
-		setup_work_tree();
+		setup_work_tree(the_repository);
 
 	if (reset_type == MIXED && is_bare_repository())
 		die(_("%s reset is not allowed in a bare repository"),
diff --git a/builtin/rev-list.c b/builtin/rev-list.c
index 854d82e..8f63003 100644
--- a/builtin/rev-list.c
+++ b/builtin/rev-list.c
@@ -25,6 +25,7 @@
 #include "oidset.h"
 #include "oidmap.h"
 #include "packfile.h"
+#include "commit-reach.h"
 #include "quote.h"
 #include "strbuf.h"
 
@@ -633,6 +634,61 @@ static int try_bitmap_disk_usage(struct rev_info *revs,
 	return 0;
 }
 
+/*
+ * If revs->maximal_only is set and no other walk modifiers are provided,
+ * run a faster computation to filter the independent commits and prepare
+ * them for output. Set revs->no_walk to prevent later walking.
+ *
+ * If this algorithm doesn't apply, then no changes are made to revs.
+ */
+static void prepare_maximal_independent(struct rev_info *revs)
+{
+	struct commit_list *c;
+
+	if (!revs->maximal_only)
+		return;
+
+	for (c = revs->commits; c; c = c->next) {
+		if (c->item->object.flags & UNINTERESTING)
+			return;
+	}
+
+	if (revs->limited ||
+	    revs->topo_order ||
+	    revs->first_parent_only ||
+	    revs->reverse ||
+	    revs->max_count >= 0 ||
+	    revs->skip_count >= 0 ||
+	    revs->min_age != (timestamp_t)-1 ||
+	    revs->max_age != (timestamp_t)-1 ||
+	    revs->min_parents > 0 ||
+	    revs->max_parents >= 0 ||
+	    revs->prune_data.nr ||
+	    revs->count ||
+	    revs->left_right ||
+	    revs->boundary ||
+	    revs->tag_objects ||
+	    revs->tree_objects ||
+	    revs->blob_objects ||
+	    revs->filter.choice ||
+	    revs->reflog_info ||
+	    revs->diff ||
+	    revs->grep_filter.pattern_list ||
+	    revs->grep_filter.header_list ||
+	    revs->verbose_header ||
+	    revs->print_parents ||
+	    revs->edge_hint ||
+	    revs->unpacked ||
+	    revs->no_kept_objects ||
+	    revs->line_level_traverse)
+		return;
+
+	reduce_heads_replace(&revs->commits);
+
+	/* Modify 'revs' to only output this commit list. */
+	revs->no_walk = 1;
+}
+
 int cmd_rev_list(int argc,
 		 const char **argv,
 		 const char *prefix,
@@ -875,6 +931,9 @@ int cmd_rev_list(int argc,
 
 	if (prepare_revision_walk(&revs))
 		die("revision walk setup failed");
+
+	prepare_maximal_independent(&revs);
+
 	if (revs.tree_objects)
 		mark_edges_uninteresting(&revs, show_edge, 0);
 
diff --git a/builtin/rev-parse.c b/builtin/rev-parse.c
index 01a6280..bb88267 100644
--- a/builtin/rev-parse.c
+++ b/builtin/rev-parse.c
@@ -10,6 +10,7 @@
 #include "builtin.h"
 
 #include "abspath.h"
+#include "bisect.h"
 #include "config.h"
 #include "commit.h"
 #include "environment.h"
@@ -267,21 +268,20 @@ static int show_file(const char *arg, int output_prefix)
 
 static int try_difference(const char *arg)
 {
-	char *dotdot;
+	const char *dotdot;
 	struct object_id start_oid;
 	struct object_id end_oid;
 	const char *end;
 	const char *start;
+	char *to_free;
 	int symmetric;
 	static const char head_by_default[] = "HEAD";
 
 	if (!(dotdot = strstr(arg, "..")))
 		return 0;
+	start = to_free = xmemdupz(arg, dotdot - arg);
 	end = dotdot + 2;
-	start = arg;
 	symmetric = (*end == '.');
-
-	*dotdot = 0;
 	end += symmetric;
 
 	if (!*end)
@@ -295,7 +295,7 @@ static int try_difference(const char *arg)
 		 * Just ".."?  That is not a range but the
 		 * pathspec for the parent directory.
 		 */
-		*dotdot = '.';
+		free(to_free);
 		return 0;
 	}
 
@@ -308,7 +308,7 @@ static int try_difference(const char *arg)
 			a = lookup_commit_reference(the_repository, &start_oid);
 			b = lookup_commit_reference(the_repository, &end_oid);
 			if (!a || !b) {
-				*dotdot = '.';
+				free(to_free);
 				return 0;
 			}
 			if (repo_get_merge_bases(the_repository, a, b, &exclude) < 0)
@@ -318,16 +318,16 @@ static int try_difference(const char *arg)
 				show_rev(REVERSED, &commit->object.oid, NULL);
 			}
 		}
-		*dotdot = '.';
+		free(to_free);
 		return 1;
 	}
-	*dotdot = '.';
+	free(to_free);
 	return 0;
 }
 
 static int try_parent_shorthands(const char *arg)
 {
-	char *dotdot;
+	const char *mark;
 	struct object_id oid;
 	struct commit *commit;
 	struct commit_list *parents;
@@ -335,38 +335,39 @@ static int try_parent_shorthands(const char *arg)
 	int include_rev = 0;
 	int include_parents = 0;
 	int exclude_parent = 0;
+	char *to_free;
 
-	if ((dotdot = strstr(arg, "^!"))) {
+	if ((mark = strstr(arg, "^!"))) {
 		include_rev = 1;
-		if (dotdot[2])
+		if (mark[2])
 			return 0;
-	} else if ((dotdot = strstr(arg, "^@"))) {
+	} else if ((mark = strstr(arg, "^@"))) {
 		include_parents = 1;
-		if (dotdot[2])
+		if (mark[2])
 			return 0;
-	} else if ((dotdot = strstr(arg, "^-"))) {
+	} else if ((mark = strstr(arg, "^-"))) {
 		include_rev = 1;
 		exclude_parent = 1;
 
-		if (dotdot[2]) {
+		if (mark[2]) {
 			char *end;
-			exclude_parent = strtoul(dotdot + 2, &end, 10);
+			exclude_parent = strtoul(mark + 2, &end, 10);
 			if (*end != '\0' || !exclude_parent)
 				return 0;
 		}
 	} else
 		return 0;
 
-	*dotdot = 0;
+	arg = to_free = xmemdupz(arg, mark - arg);
 	if (repo_get_oid_committish(the_repository, arg, &oid) ||
 	    !(commit = lookup_commit_reference(the_repository, &oid))) {
-		*dotdot = '^';
+		free(to_free);
 		return 0;
 	}
 
 	if (exclude_parent &&
 	    exclude_parent > commit_list_count(commit->parents)) {
-		*dotdot = '^';
+		free(to_free);
 		return 0;
 	}
 
@@ -387,7 +388,7 @@ static int try_parent_shorthands(const char *arg)
 		free(name);
 	}
 
-	*dotdot = '^';
+	free(to_free);
 	return 1;
 }
 
@@ -739,7 +740,7 @@ int cmd_rev_parse(int argc,
 
 	/* No options; just report on whether we're in a git repo or not. */
 	if (argc == 1) {
-		setup_git_directory();
+		setup_git_directory(the_repository);
 		repo_config(the_repository, git_default_config, NULL);
 		return 0;
 	}
@@ -749,7 +750,7 @@ int cmd_rev_parse(int argc,
 
 		if (as_is) {
 			if (show_file(arg, output_prefix) && as_is < 2)
-				verify_filename(prefix, arg, 0);
+				verify_filename(the_repository, prefix, arg, 0);
 			continue;
 		}
 
@@ -774,7 +775,7 @@ int cmd_rev_parse(int argc,
 
 		/* The rest of the options require a git repository. */
 		if (!did_repo_setup) {
-			prefix = setup_git_directory();
+			prefix = setup_git_directory(the_repository);
 			repo_config(the_repository, git_default_config, NULL);
 			did_repo_setup = 1;
 
@@ -940,13 +941,23 @@ int cmd_rev_parse(int argc,
 				continue;
 			}
 			if (!strcmp(arg, "--bisect")) {
+				char *prefix;
+				char *term_bad = NULL;
+				char *term_good = NULL;
 				struct refs_for_each_ref_options opts = { 0 };
-				opts.prefix = "refs/bisect/bad";
+				read_bisect_terms(&term_bad, &term_good);
+				prefix = xstrfmt("refs/bisect/%s", term_bad);
+				opts.prefix = prefix;
 				refs_for_each_ref_ext(get_main_ref_store(the_repository),
 						      show_reference, NULL, &opts);
-				opts.prefix = "refs/bisect/good";
+				free(prefix);
+				prefix = xstrfmt("refs/bisect/%s", term_good);
+				opts.prefix = prefix;
 				refs_for_each_ref_ext(get_main_ref_store(the_repository),
 						      anti_reference, NULL, &opts);
+				free(prefix);
+				free(term_good);
+				free(term_bad);
 				continue;
 			}
 			if (opt_with_value(arg, "--branches", &arg)) {
@@ -1006,7 +1017,7 @@ int cmd_rev_parse(int argc,
 			}
 			if (!strcmp(arg, "--show-cdup")) {
 				const char *pfx = prefix;
-				if (!is_inside_work_tree()) {
+				if (!is_inside_work_tree(the_repository)) {
 					const char *work_tree =
 						repo_get_work_tree(the_repository);
 					if (work_tree)
@@ -1063,12 +1074,12 @@ int cmd_rev_parse(int argc,
 				continue;
 			}
 			if (!strcmp(arg, "--is-inside-git-dir")) {
-				printf("%s\n", is_inside_git_dir() ? "true"
+				printf("%s\n", is_inside_git_dir(the_repository) ? "true"
 						: "false");
 				continue;
 			}
 			if (!strcmp(arg, "--is-inside-work-tree")) {
-				printf("%s\n", is_inside_work_tree() ? "true"
+				printf("%s\n", is_inside_work_tree(the_repository) ? "true"
 						: "false");
 				continue;
 			}
@@ -1173,7 +1184,7 @@ int cmd_rev_parse(int argc,
 		as_is = 1;
 		if (!show_file(arg, output_prefix))
 			continue;
-		verify_filename(prefix, arg, 1);
+		verify_filename(the_repository, prefix, arg, 1);
 	}
 	strbuf_release(&buf);
 	if (verify) {
diff --git a/builtin/rm.c b/builtin/rm.c
index 05d89e9..081d0bc 100644
--- a/builtin/rm.c
+++ b/builtin/rm.c
@@ -296,7 +296,7 @@ int cmd_rm(int argc,
 		die(_("No pathspec was given. Which files should I remove?"));
 
 	if (!index_only)
-		setup_work_tree();
+		setup_work_tree(the_repository);
 
 	prepare_repo_settings(the_repository);
 	the_repository->settings.command_requires_full_index = 0;
diff --git a/builtin/send-pack.c b/builtin/send-pack.c
index 8b81c8a..1412b49 100644
--- a/builtin/send-pack.c
+++ b/builtin/send-pack.c
@@ -273,8 +273,9 @@ int cmd_send_pack(int argc,
 		fd[0] = 0;
 		fd[1] = 1;
 	} else {
-		conn = git_connect(fd, dest, "git-receive-pack", receivepack,
-			args.verbose ? CONNECT_VERBOSE : 0);
+		conn = git_connect(fd, dest, GIT_CONNECT_RECEIVE_PACK,
+				   receivepack,
+				   args.verbose ? CONNECT_VERBOSE : 0);
 	}
 
 	packet_reader_init(&reader, fd[0], NULL, 0,
diff --git a/builtin/show-ref.c b/builtin/show-ref.c
index 5d31ace..d508441 100644
--- a/builtin/show-ref.c
+++ b/builtin/show-ref.c
@@ -37,7 +37,7 @@ static void show_one(const struct show_one_options *opts,
 	struct object_id peeled;
 
 	if (!odb_has_object(the_repository->objects, ref->oid,
-			    HAS_OBJECT_RECHECK_PACKED | HAS_OBJECT_FETCH_PROMISOR))
+			    ODB_HAS_OBJECT_RECHECK_PACKED | ODB_HAS_OBJECT_FETCH_PROMISOR))
 		die("git show-ref: bad ref %s (%s)", ref->name,
 		    oid_to_hex(ref->oid));
 
diff --git a/builtin/sparse-checkout.c b/builtin/sparse-checkout.c
index f4aa405..d89acbe 100644
--- a/builtin/sparse-checkout.c
+++ b/builtin/sparse-checkout.c
@@ -63,7 +63,7 @@ static int sparse_checkout_list(int argc, const char **argv, const char *prefix,
 	int res;
 	struct repo_config_values *cfg = repo_config_values(the_repository);
 
-	setup_work_tree();
+	setup_work_tree(the_repository);
 	if (!cfg->apply_sparse_checkout)
 		die(_("this worktree is not sparse"));
 
@@ -229,7 +229,7 @@ static int update_working_directory(struct repository *r,
 	o.dst_index = r->index;
 	o.skip_sparse_checkout = 0;
 
-	setup_work_tree();
+	setup_work_tree(the_repository);
 
 	repo_hold_locked_index(r, &lock_file, LOCK_DIE_ON_ERROR);
 
@@ -468,7 +468,7 @@ static int sparse_checkout_init(int argc, const char **argv, const char *prefix,
 		OPT_END(),
 	};
 
-	setup_work_tree();
+	setup_work_tree(the_repository);
 	repo_read_index(repo);
 
 	init_opts.cone_mode = -1;
@@ -735,7 +735,8 @@ static void sanitize_paths(struct repository *repo,
 		int prefix_len = strlen(prefix);
 
 		for (i = 0; i < args->nr; i++) {
-			char *prefixed_path = prefix_path(prefix, prefix_len, args->v[i]);
+			char *prefixed_path = prefix_path(the_repository, prefix,
+							  prefix_len, args->v[i]);
 			strvec_replace(args, i, prefixed_path);
 			free(prefixed_path);
 		}
@@ -801,7 +802,7 @@ static int sparse_checkout_add(int argc, const char **argv, const char *prefix,
 	int ret;
 	struct repo_config_values *cfg = repo_config_values(the_repository);
 
-	setup_work_tree();
+	setup_work_tree(the_repository);
 	if (!cfg->apply_sparse_checkout)
 		die(_("no sparse-checkout to add to"));
 
@@ -855,7 +856,7 @@ static int sparse_checkout_set(int argc, const char **argv, const char *prefix,
 	struct strvec patterns = STRVEC_INIT;
 	int ret;
 
-	setup_work_tree();
+	setup_work_tree(the_repository);
 	repo_read_index(repo);
 
 	set_opts.cone_mode = -1;
@@ -911,7 +912,7 @@ static int sparse_checkout_reapply(int argc, const char **argv,
 	};
 	struct repo_config_values *cfg = repo_config_values(the_repository);
 
-	setup_work_tree();
+	setup_work_tree(the_repository);
 	if (!cfg->apply_sparse_checkout)
 		die(_("must be in a sparse-checkout to reapply sparsity patterns"));
 
@@ -974,7 +975,7 @@ static int sparse_checkout_clean(int argc, const char **argv,
 		OPT_END(),
 	};
 
-	setup_work_tree();
+	setup_work_tree(the_repository);
 	if (!cfg->apply_sparse_checkout)
 		die(_("must be in a sparse-checkout to clean directories"));
 	if (!core_sparse_checkout_cone)
@@ -1052,7 +1053,7 @@ static int sparse_checkout_disable(int argc, const char **argv,
 	 * forcibly return to a dense checkout regardless of initial state.
 	 */
 
-	setup_work_tree();
+	setup_work_tree(the_repository);
 	argc = parse_options(argc, argv, prefix,
 			     builtin_sparse_checkout_disable_options,
 			     builtin_sparse_checkout_disable_usage, 0);
diff --git a/builtin/stash.c b/builtin/stash.c
index 95c5005..c4809f2 100644
--- a/builtin/stash.c
+++ b/builtin/stash.c
@@ -44,16 +44,16 @@
 #define BUILTIN_STASH_POP_USAGE \
 	N_("git stash pop [--index] [-q | --quiet] [<stash>]")
 #define BUILTIN_STASH_APPLY_USAGE \
-	N_("git stash apply [--index] [-q | --quiet] [<stash>]")
+	N_("git stash apply [--index] [-q | --quiet] [--label-ours=<label>] [--label-theirs=<label>] [--label-base=<label>] [<stash>]")
 #define BUILTIN_STASH_BRANCH_USAGE \
 	N_("git stash branch <branchname> [<stash>]")
 #define BUILTIN_STASH_STORE_USAGE \
 	N_("git stash store [(-m | --message) <message>] [-q | --quiet] <commit>")
 #define BUILTIN_STASH_PUSH_USAGE \
-	N_("git stash [push [-p | --patch] [-S | --staged] [-k | --[no-]keep-index] [-q | --quiet]\n" \
+	N_("git stash [push] [-p | --patch] [-S | --staged] [-k | --[no-]keep-index] [-q | --quiet]\n" \
 	   "          [-u | --include-untracked] [-a | --all] [(-m | --message) <message>]\n" \
 	   "          [--pathspec-from-file=<file> [--pathspec-file-nul]]\n" \
-	   "          [--] [<pathspec>...]]")
+	   "          [--] [<pathspec>...]")
 #define BUILTIN_STASH_SAVE_USAGE \
 	N_("git stash save [-p | --patch] [-S | --staged] [-k | --[no-]keep-index] [-q | --quiet]\n" \
 	   "          [-u | --include-untracked] [-a | --all] [<message>]")
@@ -372,6 +372,56 @@ static int reset_tree(struct object_id *i_tree, int update, int reset)
 	return 0;
 }
 
+static int create_index_from_tree(const struct object_id *tree_id,
+				  const char *index_path)
+{
+	int nr_trees = 1;
+	int ret = 0;
+	struct unpack_trees_options opts;
+	struct tree_desc t[MAX_UNPACK_TREES];
+	struct tree *tree;
+	struct index_state dst_istate = INDEX_STATE_INIT(the_repository);
+	struct lock_file lock_file = LOCK_INIT;
+
+	repo_read_index_preload(the_repository, NULL, 0);
+	refresh_index(the_repository->index, REFRESH_QUIET, NULL, NULL, NULL);
+
+	hold_lock_file_for_update(&lock_file, index_path, LOCK_DIE_ON_ERROR);
+
+	memset(&opts, 0, sizeof(opts));
+
+	tree = repo_parse_tree_indirect(the_repository, tree_id);
+	if (!tree || repo_parse_tree(the_repository, tree)) {
+		ret = -1;
+		goto done;
+	}
+
+	init_tree_desc(t, &tree->object.oid, tree->buffer, tree->size);
+
+	opts.head_idx = 1;
+	opts.src_index = the_repository->index;
+	opts.dst_index = &dst_istate;
+	opts.merge = 1;
+	opts.reset = UNPACK_RESET_PROTECT_UNTRACKED;
+	opts.fn = oneway_merge;
+
+	if (unpack_trees(nr_trees, t, &opts)) {
+		ret = -1;
+		goto done;
+	}
+
+	if (write_locked_index(&dst_istate, &lock_file, COMMIT_LOCK)) {
+		ret = error(_("unable to write new index file"));
+		goto done;
+	}
+
+done:
+	release_index(&dst_istate);
+	if (ret)
+		rollback_lock_file(&lock_file);
+	return ret;
+}
+
 static int diff_tree_binary(struct strbuf *out, struct object_id *w_commit)
 {
 	struct child_process cp = CHILD_PROCESS_INIT;
@@ -591,7 +641,9 @@ static void unstage_changes_unless_new(struct object_id *orig_tree)
 }
 
 static int do_apply_stash(const char *prefix, struct stash_info *info,
-			  int index, int quiet)
+			  int index, int quiet,
+			  const char *label_ours, const char *label_theirs,
+			  const char *label_base)
 {
 	int clean, ret;
 	int has_index = index;
@@ -643,9 +695,9 @@ static int do_apply_stash(const char *prefix, struct stash_info *info,
 
 	init_ui_merge_options(&o, the_repository);
 
-	o.branch1 = "Updated upstream";
-	o.branch2 = "Stashed changes";
-	o.ancestor = "Stash base";
+	o.branch1 = label_ours ? label_ours : "Updated upstream";
+	o.branch2 = label_theirs ? label_theirs : "Stashed changes";
+	o.ancestor = label_base ? label_base : "Stash base";
 
 	if (oideq(&info->b_tree, &c_tree))
 		o.branch1 = "Version stash was based on";
@@ -723,11 +775,18 @@ static int apply_stash(int argc, const char **argv, const char *prefix,
 	int ret = -1;
 	int quiet = 0;
 	int index = use_index;
+	const char *label_ours = NULL, *label_theirs = NULL, *label_base = NULL;
 	struct stash_info info = STASH_INFO_INIT;
 	struct option options[] = {
 		OPT__QUIET(&quiet, N_("be quiet, only report errors")),
 		OPT_BOOL(0, "index", &index,
 			 N_("attempt to recreate the index")),
+		OPT_STRING(0, "label-ours", &label_ours, N_("label"),
+			   N_("label for the upstream side in conflict markers")),
+		OPT_STRING(0, "label-theirs", &label_theirs, N_("label"),
+			   N_("label for the stashed side in conflict markers")),
+		OPT_STRING(0, "label-base", &label_base, N_("label"),
+			   N_("label for the base in diff3 conflict markers")),
 		OPT_END()
 	};
 
@@ -737,7 +796,8 @@ static int apply_stash(int argc, const char **argv, const char *prefix,
 	if (get_stash_info(&info, argc, argv))
 		goto cleanup;
 
-	ret = do_apply_stash(prefix, &info, index, quiet);
+	ret = do_apply_stash(prefix, &info, index, quiet,
+			     label_ours, label_theirs, label_base);
 cleanup:
 	free_stash_info(&info);
 	return ret;
@@ -836,7 +896,8 @@ static int pop_stash(int argc, const char **argv, const char *prefix,
 	if (get_stash_info_assert(&info, argc, argv))
 		goto cleanup;
 
-	if ((ret = do_apply_stash(prefix, &info, index, quiet)))
+	if ((ret = do_apply_stash(prefix, &info, index, quiet,
+				  NULL, NULL, NULL)))
 		printf_ln(_("The stash entry is kept in case "
 			    "you need it again."));
 	else
@@ -877,7 +938,8 @@ static int branch_stash(int argc, const char **argv, const char *prefix,
 	strvec_push(&cp.args, oid_to_hex(&info.b_commit));
 	ret = run_command(&cp);
 	if (!ret)
-		ret = do_apply_stash(prefix, &info, 1, 0);
+		ret = do_apply_stash(prefix, &info, 1, 0,
+				     NULL, NULL, NULL);
 	if (!ret && info.is_stash_ref)
 		ret = do_drop_stash(&info, 0);
 
@@ -1309,18 +1371,26 @@ static int stash_patch(struct stash_info *info, const struct pathspec *ps,
 		       struct interactive_options *interactive_opts)
 {
 	int ret = 0;
-	struct child_process cp_read_tree = CHILD_PROCESS_INIT;
 	struct child_process cp_diff_tree = CHILD_PROCESS_INIT;
+	struct commit *head_commit;
+	const struct object_id *head_tree;
 	struct index_state istate = INDEX_STATE_INIT(the_repository);
 	char *old_index_env = NULL, *old_repo_index_file;
 
 	remove_path(stash_index_path.buf);
 
-	cp_read_tree.git_cmd = 1;
-	strvec_pushl(&cp_read_tree.args, "read-tree", "HEAD", NULL);
-	strvec_pushf(&cp_read_tree.env, "GIT_INDEX_FILE=%s",
-		     stash_index_path.buf);
-	if (run_command(&cp_read_tree)) {
+	head_commit = lookup_commit(the_repository, &info->b_commit);
+	if (!head_commit || repo_parse_commit(the_repository, head_commit)) {
+		ret = -1;
+		goto done;
+	}
+	head_tree = get_commit_tree_oid(head_commit);
+	if (!head_tree) {
+		ret = -1;
+		goto done;
+	}
+
+	if (create_index_from_tree(head_tree, stash_index_path.buf)) {
 		ret = -1;
 		goto done;
 	}
diff --git a/builtin/stripspace.c b/builtin/stripspace.c
index 4a566cb..18705f1 100644
--- a/builtin/stripspace.c
+++ b/builtin/stripspace.c
@@ -54,7 +54,7 @@ int cmd_stripspace(int argc,
 		usage_with_options(stripspace_usage, options);
 
 	if (mode == STRIP_COMMENTS || mode == COMMENT_LINES) {
-		setup_git_directory_gently(&nongit);
+		setup_git_directory_gently(the_repository, &nongit);
 		repo_config(the_repository, git_default_config, NULL);
 	}
 
diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 2f589e3..1cc82a1 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -1250,7 +1250,7 @@ static int compute_summary_module_list(struct object_id *head_oid,
 
 	if (!info->cached) {
 		if (diff_cmd == DIFF_INDEX)
-			setup_work_tree();
+			setup_work_tree(the_repository);
 		if (repo_read_index_preload(the_repository, &rev.diffopt.pathspec, 0) < 0) {
 			perror("repo_read_index_preload");
 			ret = -1;
diff --git a/builtin/unpack-objects.c b/builtin/unpack-objects.c
index 6fc64e9..59e9b87 100644
--- a/builtin/unpack-objects.c
+++ b/builtin/unpack-objects.c
@@ -9,6 +9,8 @@
 #include "hex.h"
 #include "object-file.h"
 #include "odb.h"
+#include "odb/streaming.h"
+#include "odb/transaction.h"
 #include "object.h"
 #include "delta.h"
 #include "pack.h"
@@ -23,13 +25,12 @@
 static int dry_run, quiet, recover, has_errors, strict;
 static const char unpack_usage[] = "git unpack-objects [-n] [-q] [-r] [--strict]";
 
-/* We always read in 4kB chunks. */
-static unsigned char buffer[4096];
+static unsigned char buffer[DEFAULT_IO_BUFFER_SIZE];
 static unsigned int offset, len;
 static off_t consumed_bytes;
 static off_t max_input_size;
 static struct git_hash_ctx ctx;
-static struct fsck_options fsck_options = FSCK_OPTIONS_STRICT;
+static struct fsck_options fsck_options;
 static struct progress *progress;
 
 /*
@@ -359,24 +360,21 @@ static void unpack_non_delta_entry(enum object_type type, unsigned long size,
 
 struct input_zstream_data {
 	git_zstream *zstream;
-	unsigned char buf[8192];
 	int status;
 };
 
-static const void *feed_input_zstream(struct odb_write_stream *in_stream,
-				      unsigned long *readlen)
+static ssize_t feed_input_zstream(struct odb_write_stream *in_stream,
+				  unsigned char *buf, size_t buf_len)
 {
 	struct input_zstream_data *data = in_stream->data;
 	git_zstream *zstream = data->zstream;
 	void *in = fill(1);
 
-	if (in_stream->is_finished) {
-		*readlen = 0;
-		return NULL;
-	}
+	if (in_stream->is_finished)
+		return 0;
 
-	zstream->next_out = data->buf;
-	zstream->avail_out = sizeof(data->buf);
+	zstream->next_out = buf;
+	zstream->avail_out = buf_len;
 	zstream->next_in = in;
 	zstream->avail_in = len;
 
@@ -384,9 +382,7 @@ static const void *feed_input_zstream(struct odb_write_stream *in_stream,
 
 	in_stream->is_finished = data->status != Z_OK;
 	use(len - zstream->avail_in);
-	*readlen = sizeof(data->buf) - zstream->avail_out;
-
-	return data->buf;
+	return buf_len - zstream->avail_out;
 }
 
 static void stream_blob(unsigned long size, unsigned nr)
@@ -449,7 +445,7 @@ static void unpack_delta_entry(enum object_type type, unsigned long delta_size,
 		if (!delta_data)
 			return;
 		if (odb_has_object(the_repository->objects, &base_oid,
-				   HAS_OBJECT_RECHECK_PACKED | HAS_OBJECT_FETCH_PROMISOR))
+				   ODB_HAS_OBJECT_RECHECK_PACKED | ODB_HAS_OBJECT_FETCH_PROMISOR))
 			; /* Ok we have this one */
 		else if (resolve_against_held(nr, &base_oid,
 					      delta_data, delta_size))
@@ -533,7 +529,7 @@ static void unpack_one(unsigned nr)
 {
 	unsigned shift;
 	unsigned char *pack;
-	unsigned long size, c;
+	size_t size, c;
 	enum object_type type;
 
 	obj_list[nr].offset = consumed_bytes;
@@ -545,6 +541,8 @@ static void unpack_one(unsigned nr)
 	size = (c & 15);
 	shift = 4;
 	while (c & 0x80) {
+		if ((bitsizeof(size_t) - 7) < shift)
+			die(_("object size too large for this platform"));
 		pack = fill(1);
 		c = *pack;
 		use(1);
@@ -613,7 +611,7 @@ static void unpack_all(void)
 int cmd_unpack_objects(int argc,
 		       const char **argv,
 		       const char *prefix UNUSED,
-		       struct repository *repo UNUSED)
+		       struct repository *repo)
 {
 	int i;
 	struct object_id oid;
@@ -627,6 +625,8 @@ int cmd_unpack_objects(int argc,
 
 	show_usage_if_asked(argc, argv, unpack_usage);
 
+	fsck_options_init(&fsck_options, repo, FSCK_OPTIONS_STRICT);
+
 	for (i = 1 ; i < argc; i++) {
 		const char *arg = argv[i];
 
diff --git a/builtin/update-index.c b/builtin/update-index.c
index 8a59077..3d6646c 100644
--- a/builtin/update-index.c
+++ b/builtin/update-index.c
@@ -19,6 +19,7 @@
 #include "tree-walk.h"
 #include "object-file.h"
 #include "odb.h"
+#include "odb/transaction.h"
 #include "refs.h"
 #include "resolve-undo.h"
 #include "parse-options.h"
@@ -655,7 +656,7 @@ static int do_unresolve(int ac, const char **av,
 
 	for (i = 1; i < ac; i++) {
 		const char *arg = av[i];
-		char *p = prefix_path(prefix, prefix_length, arg);
+		char *p = prefix_path(the_repository, prefix, prefix_length, arg);
 		err |= unresolve_one(p);
 		free(p);
 	}
@@ -732,7 +733,7 @@ struct refresh_params {
 
 static int refresh(struct refresh_params *o, unsigned int flag)
 {
-	setup_work_tree();
+	setup_work_tree(the_repository);
 	repo_read_index(the_repository);
 	*o->has_errors |= refresh_index(the_repository->index, o->flags | flag, NULL,
 					NULL, NULL);
@@ -901,7 +902,7 @@ static enum parse_opt_result reupdate_callback(
 	BUG_ON_OPT_ARG(arg);
 
 	/* consume remaining arguments. */
-	setup_work_tree();
+	setup_work_tree(the_repository);
 	*has_errors = do_reupdate(ctx->argv + 1, prefix);
 	if (*has_errors)
 		the_repository->index->cache_changed = 0;
@@ -1157,8 +1158,8 @@ int cmd_update_index(int argc,
 				transaction = NULL;
 			}
 
-			setup_work_tree();
-			p = prefix_path(prefix, prefix_length, path);
+			setup_work_tree(the_repository);
+			p = prefix_path(the_repository, prefix, prefix_length, path);
 			update_one(p);
 			if (set_executable_bit)
 				chmod_path(set_executable_bit, p);
@@ -1199,7 +1200,7 @@ int cmd_update_index(int argc,
 		struct strbuf buf = STRBUF_INIT;
 		struct strbuf unquoted = STRBUF_INIT;
 
-		setup_work_tree();
+		setup_work_tree(the_repository);
 		while (getline_fn(&buf, stdin) != EOF) {
 			char *p;
 			if (!nul_term_line && buf.buf[0] == '"') {
@@ -1208,7 +1209,7 @@ int cmd_update_index(int argc,
 					die("line is badly quoted");
 				strbuf_swap(&buf, &unquoted);
 			}
-			p = prefix_path(prefix, prefix_length, buf.buf);
+			p = prefix_path(the_repository, prefix, prefix_length, buf.buf);
 			update_one(p);
 			if (set_executable_bit)
 				chmod_path(set_executable_bit, p);
@@ -1253,7 +1254,7 @@ int cmd_update_index(int argc,
 		report(_("Untracked cache disabled"));
 		break;
 	case UC_TEST:
-		setup_work_tree();
+		setup_work_tree(the_repository);
 		return !test_if_untracked_cache_is_supported();
 	case UC_ENABLE:
 	case UC_FORCE:
diff --git a/builtin/update-ref.c b/builtin/update-ref.c
index 2d68c40..6355c3d 100644
--- a/builtin/update-ref.c
+++ b/builtin/update-ref.c
@@ -25,6 +25,15 @@ static unsigned int default_flags;
 static unsigned create_reflog_flag;
 static const char *msg;
 
+struct command_options {
+	/*
+	 * Individual updates are allowed to fail without causing
+	 * update-ref to exit. This is set when using the
+	 * '--batch-updates' flag.
+	 */
+	bool allow_update_failures;
+};
+
 /*
  * Parse one whitespace- or NUL-terminated, possibly C-quoted argument
  * and append the result to arg.  Return a pointer to the terminator.
@@ -234,6 +243,53 @@ static int parse_next_oid(const char **next, const char *end,
 	    command, refname);
 }
 
+static void print_rejected_refs(const char *refname,
+				const struct object_id *old_oid,
+				const struct object_id *new_oid,
+				const char *old_target,
+				const char *new_target,
+				enum ref_transaction_error err,
+				const char *details,
+				void *cb_data UNUSED)
+{
+	struct strbuf sb = STRBUF_INIT;
+
+	if (details && *details)
+		error("%s", details);
+
+	strbuf_addf(&sb, "rejected %s %s %s %s\n", refname,
+		    new_oid ? oid_to_hex(new_oid) : new_target,
+		    old_oid ? oid_to_hex(old_oid) : old_target,
+		    ref_transaction_error_msg(err));
+
+	fwrite(sb.buf, sb.len, 1, stdout);
+	strbuf_release(&sb);
+}
+
+/*
+ * Handle transaction errors. If we're using batches updates, we want to only
+ * die for generic errors and print the remaining to the user.
+ */
+static void handle_ref_transaction_error(const char *refname,
+					 struct object_id *new_oid,
+					 struct object_id *old_oid,
+					 const char *new_target,
+					 const char *old_target,
+					 enum ref_transaction_error tx_err,
+					 struct strbuf *err,
+					 struct command_options *opts)
+{
+	if (!tx_err)
+		return;
+
+	if (tx_err != REF_TRANSACTION_ERROR_GENERIC && opts->allow_update_failures) {
+		print_rejected_refs(refname, old_oid, new_oid, old_target,
+				    new_target, tx_err, err->buf, NULL);
+		return;
+	}
+
+	die("%s", err->buf);
+}
 
 /*
  * The following five parse_cmd_*() functions parse the corresponding
@@ -246,11 +302,13 @@ static int parse_next_oid(const char **next, const char *end,
  */
 
 static void parse_cmd_update(struct ref_transaction *transaction,
-			     const char *next, const char *end)
+			     const char *next, const char *end,
+			     struct command_options *opts)
 {
 	struct strbuf err = STRBUF_INIT;
 	char *refname;
 	struct object_id new_oid, old_oid;
+	enum ref_transaction_error tx_err;
 	int have_old;
 
 	refname = parse_refname(&next);
@@ -267,12 +325,14 @@ static void parse_cmd_update(struct ref_transaction *transaction,
 	if (*next != line_termination)
 		die("update %s: extra input: %s", refname, next);
 
-	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);
+	tx_err = ref_transaction_update(transaction, refname,
+					&new_oid, have_old ? &old_oid : NULL,
+					NULL, NULL,
+					update_flags | create_reflog_flag,
+					msg, &err);
+	handle_ref_transaction_error(refname, &new_oid, have_old ? &old_oid : NULL,
+				     NULL, NULL, tx_err, &err, opts);
+
 
 	update_flags = default_flags;
 	free(refname);
@@ -280,9 +340,11 @@ static void parse_cmd_update(struct ref_transaction *transaction,
 }
 
 static void parse_cmd_symref_update(struct ref_transaction *transaction,
-				    const char *next, const char *end UNUSED)
+				    const char *next, const char *end UNUSED,
+				    struct command_options *opts)
 {
 	char *refname, *new_target, *old_arg;
+	enum ref_transaction_error tx_err;
 	char *old_target = NULL;
 	struct strbuf err = STRBUF_INIT;
 	struct object_id old_oid;
@@ -319,13 +381,15 @@ static void parse_cmd_symref_update(struct ref_transaction *transaction,
 	if (*next != line_termination)
 		die("symref-update %s: extra input: %s", refname, next);
 
-	if (ref_transaction_update(transaction, refname, NULL,
-				   have_old_oid ? &old_oid : NULL,
-				   new_target,
-				   have_old_oid ? NULL : old_target,
-				   update_flags | create_reflog_flag,
-				   msg, &err))
-		die("%s", err.buf);
+	tx_err = ref_transaction_update(transaction, refname, NULL,
+					have_old_oid ? &old_oid : NULL,
+					new_target,
+					have_old_oid ? NULL : old_target,
+					update_flags | create_reflog_flag,
+					msg, &err);
+	handle_ref_transaction_error(refname, NULL, have_old_oid ? &old_oid : NULL,
+				     new_target, have_old_oid ? NULL : old_target,
+				     tx_err, &err, opts);
 
 	update_flags = default_flags;
 	free(refname);
@@ -336,11 +400,13 @@ static void parse_cmd_symref_update(struct ref_transaction *transaction,
 }
 
 static void parse_cmd_create(struct ref_transaction *transaction,
-			     const char *next, const char *end)
+			     const char *next, const char *end,
+			     struct command_options *opts)
 {
 	struct strbuf err = STRBUF_INIT;
 	char *refname;
 	struct object_id new_oid;
+	enum ref_transaction_error tx_err;
 
 	refname = parse_refname(&next);
 	if (!refname)
@@ -355,22 +421,24 @@ static void parse_cmd_create(struct ref_transaction *transaction,
 	if (*next != line_termination)
 		die("create %s: extra input: %s", refname, next);
 
-	if (ref_transaction_create(transaction, refname, &new_oid, NULL,
-				   update_flags | create_reflog_flag,
-				   msg, &err))
-		die("%s", err.buf);
+	tx_err = ref_transaction_create(transaction, refname, &new_oid, NULL,
+					update_flags | create_reflog_flag,
+					msg, &err);
+	handle_ref_transaction_error(refname, &new_oid, NULL, NULL, NULL, tx_err,
+				     &err, opts);
 
 	update_flags = default_flags;
 	free(refname);
 	strbuf_release(&err);
 }
 
-
 static void parse_cmd_symref_create(struct ref_transaction *transaction,
-				    const char *next, const char *end UNUSED)
+				    const char *next, const char *end UNUSED,
+				    struct command_options *opts)
 {
 	struct strbuf err = STRBUF_INIT;
 	char *refname, *new_target;
+	enum ref_transaction_error tx_err;
 
 	refname = parse_refname(&next);
 	if (!refname)
@@ -383,10 +451,11 @@ static void parse_cmd_symref_create(struct ref_transaction *transaction,
 	if (*next != line_termination)
 		die("symref-create %s: extra input: %s", refname, next);
 
-	if (ref_transaction_create(transaction, refname, NULL, new_target,
-				   update_flags | create_reflog_flag,
-				   msg, &err))
-		die("%s", err.buf);
+	tx_err = ref_transaction_create(transaction, refname, NULL, new_target,
+					update_flags | create_reflog_flag,
+					msg, &err);
+	handle_ref_transaction_error(refname, NULL, NULL, new_target, NULL,
+				     tx_err, &err, opts);
 
 	update_flags = default_flags;
 	free(refname);
@@ -395,7 +464,8 @@ static void parse_cmd_symref_create(struct ref_transaction *transaction,
 }
 
 static void parse_cmd_delete(struct ref_transaction *transaction,
-			     const char *next, const char *end)
+			     const char *next, const char *end,
+			     struct command_options *opts UNUSED)
 {
 	struct strbuf err = STRBUF_INIT;
 	char *refname;
@@ -428,9 +498,9 @@ static void parse_cmd_delete(struct ref_transaction *transaction,
 	strbuf_release(&err);
 }
 
-
 static void parse_cmd_symref_delete(struct ref_transaction *transaction,
-				    const char *next, const char *end UNUSED)
+				    const char *next, const char *end UNUSED,
+				    struct command_options *opts UNUSED)
 {
 	struct strbuf err = STRBUF_INIT;
 	char *refname, *old_target;
@@ -457,9 +527,9 @@ static void parse_cmd_symref_delete(struct ref_transaction *transaction,
 	strbuf_release(&err);
 }
 
-
 static void parse_cmd_verify(struct ref_transaction *transaction,
-			     const char *next, const char *end)
+			     const char *next, const char *end,
+			     struct command_options *opts UNUSED)
 {
 	struct strbuf err = STRBUF_INIT;
 	char *refname;
@@ -486,7 +556,8 @@ static void parse_cmd_verify(struct ref_transaction *transaction,
 }
 
 static void parse_cmd_symref_verify(struct ref_transaction *transaction,
-				    const char *next, const char *end UNUSED)
+				    const char *next, const char *end UNUSED,
+				    struct command_options *opts UNUSED)
 {
 	struct strbuf err = STRBUF_INIT;
 	struct object_id old_oid;
@@ -528,7 +599,8 @@ static void report_ok(const char *command)
 }
 
 static void parse_cmd_option(struct ref_transaction *transaction UNUSED,
-			     const char *next, const char *end UNUSED)
+			     const char *next, const char *end UNUSED,
+			     struct command_options *opts UNUSED)
 {
 	const char *rest;
 	if (skip_prefix(next, "no-deref", &rest) && *rest == line_termination)
@@ -538,7 +610,8 @@ static void parse_cmd_option(struct ref_transaction *transaction UNUSED,
 }
 
 static void parse_cmd_start(struct ref_transaction *transaction UNUSED,
-			    const char *next, const char *end UNUSED)
+			    const char *next, const char *end UNUSED,
+			    struct command_options *opts UNUSED)
 {
 	if (*next != line_termination)
 		die("start: extra input: %s", next);
@@ -546,7 +619,8 @@ static void parse_cmd_start(struct ref_transaction *transaction UNUSED,
 }
 
 static void parse_cmd_prepare(struct ref_transaction *transaction,
-			      const char *next, const char *end UNUSED)
+			      const char *next, const char *end UNUSED,
+			      struct command_options *opts UNUSED)
 {
 	struct strbuf error = STRBUF_INIT;
 	if (*next != line_termination)
@@ -557,7 +631,8 @@ static void parse_cmd_prepare(struct ref_transaction *transaction,
 }
 
 static void parse_cmd_abort(struct ref_transaction *transaction,
-			    const char *next, const char *end UNUSED)
+			    const char *next, const char *end UNUSED,
+			    struct command_options *opts UNUSED)
 {
 	struct strbuf error = STRBUF_INIT;
 	if (*next != line_termination)
@@ -567,31 +642,9 @@ static void parse_cmd_abort(struct ref_transaction *transaction,
 	report_ok("abort");
 }
 
-static void print_rejected_refs(const char *refname,
-				const struct object_id *old_oid,
-				const struct object_id *new_oid,
-				const char *old_target,
-				const char *new_target,
-				enum ref_transaction_error err,
-				const char *details,
-				void *cb_data UNUSED)
-{
-	struct strbuf sb = STRBUF_INIT;
-
-	if (details && *details)
-		error("%s", details);
-
-	strbuf_addf(&sb, "rejected %s %s %s %s\n", refname,
-		    new_oid ? oid_to_hex(new_oid) : new_target,
-		    old_oid ? oid_to_hex(old_oid) : old_target,
-		    ref_transaction_error_msg(err));
-
-	fwrite(sb.buf, sb.len, 1, stdout);
-	strbuf_release(&sb);
-}
-
 static void parse_cmd_commit(struct ref_transaction *transaction,
-			     const char *next, const char *end UNUSED)
+			     const char *next, const char *end UNUSED,
+			     struct command_options *opts UNUSED)
 {
 	struct strbuf error = STRBUF_INIT;
 	if (*next != line_termination)
@@ -619,7 +672,8 @@ enum update_refs_state {
 
 static const struct parse_cmd {
 	const char *prefix;
-	void (*fn)(struct ref_transaction *, const char *, const char *);
+	void (*fn)(struct ref_transaction *, const char *, const char *,
+		   struct command_options *);
 	unsigned args;
 	enum update_refs_state state;
 } command[] = {
@@ -645,6 +699,10 @@ static void update_refs_stdin(unsigned int flags)
 	struct ref_transaction *transaction;
 	int i, j;
 
+	struct command_options opts = {
+		.allow_update_failures = flags & REF_TRANSACTION_ALLOW_FAILURE,
+	};
+
 	transaction = ref_store_transaction_begin(get_main_ref_store(the_repository),
 						  flags, &err);
 	if (!transaction)
@@ -722,7 +780,7 @@ static void update_refs_stdin(unsigned int flags)
 		}
 
 		cmd->fn(transaction, input.buf + strlen(cmd->prefix) + !!cmd->args,
-			input.buf + input.len);
+			input.buf + input.len, &opts);
 	}
 
 	switch (state) {
diff --git a/builtin/upload-archive.c b/builtin/upload-archive.c
index 25312bb..718e74b 100644
--- a/builtin/upload-archive.c
+++ b/builtin/upload-archive.c
@@ -31,7 +31,7 @@ int cmd_upload_archive_writer(int argc,
 	if (argc != 2)
 		usage(upload_archive_usage);
 
-	if (!enter_repo(argv[1], 0))
+	if (!enter_repo(the_repository, argv[1], 0))
 		die("'%s' does not appear to be a git repository", argv[1]);
 
 	init_archivers();
diff --git a/builtin/upload-pack.c b/builtin/upload-pack.c
index 30498fa..32831fb 100644
--- a/builtin/upload-pack.c
+++ b/builtin/upload-pack.c
@@ -59,7 +59,7 @@ int cmd_upload_pack(int argc,
 
 	if (strict)
 		enter_repo_flags |= ENTER_REPO_STRICT;
-	if (!enter_repo(dir, enter_repo_flags))
+	if (!enter_repo(the_repository, dir, enter_repo_flags))
 		die("'%s' does not appear to be a git repository", dir);
 
 	switch (determine_protocol_version_server()) {
diff --git a/builtin/url-parse.c b/builtin/url-parse.c
new file mode 100644
index 0000000..7e70553
--- /dev/null
+++ b/builtin/url-parse.c
@@ -0,0 +1,135 @@
+#include "builtin.h"
+#include "gettext.h"
+#include "parse-options.h"
+#include "url.h"
+#include "urlmatch.h"
+
+static const char * const builtin_url_parse_usage[] = {
+	N_("git url-parse [-c <component>] [--] <url>..."),
+	NULL
+};
+
+static char *component_arg;
+
+static struct option builtin_url_parse_options[] = {
+	OPT_STRING('c', "component", &component_arg, N_("component"),
+		N_("which URL component to extract")),
+	OPT_END(),
+};
+
+enum url_component {
+	URL_NONE = 0,
+	URL_SCHEME,
+	URL_USER,
+	URL_PASSWORD,
+	URL_HOST,
+	URL_PORT,
+	URL_PATH,
+};
+
+static void parse_or_die(const char *url, struct url_info *info)
+{
+	if (url_is_local_not_ssh(url)) {
+		if (*url == '/')
+			die("'%s' is not a URL; if you meant a local "
+			    "repository, use 'file://%s'", url, url);
+		if (has_dos_drive_prefix(url))
+			die("'%s' is not a URL; if you meant a local "
+			    "repository, use 'file:///%s'", url, url);
+		die("'%s' is not a URL; if you meant a local repository, "
+		    "use a 'file://' URL with an absolute path", url);
+	}
+	if (!url_parse(url, info))
+		die("invalid git URL '%s': %s", url, info->err);
+}
+
+static enum url_component get_component_or_die(const char *arg)
+{
+	if (!strcmp("path", arg))
+		return URL_PATH;
+	if (!strcmp("host", arg))
+		return URL_HOST;
+	if (!strcmp("scheme", arg))
+		return URL_SCHEME;
+	if (!strcmp("user", arg))
+		return URL_USER;
+	if (!strcmp("password", arg))
+		return URL_PASSWORD;
+	if (!strcmp("port", arg))
+		return URL_PORT;
+	die("invalid git URL component '%s'", arg);
+}
+
+static char *extract_component(enum url_component component,
+			       struct url_info *info)
+{
+	size_t offset, length;
+
+	switch (component) {
+	case URL_SCHEME:
+		offset = 0;
+		length = info->scheme_len;
+		break;
+	case URL_USER:
+		offset = info->user_off;
+		length = info->user_len;
+		break;
+	case URL_PASSWORD:
+		offset = info->passwd_off;
+		length = info->passwd_len;
+		break;
+	case URL_HOST:
+		offset = info->host_off;
+		length = info->host_len;
+		break;
+	case URL_PORT:
+		offset = info->port_off;
+		length = info->port_len;
+		break;
+	case URL_PATH:
+		offset = info->path_off;
+		length = info->path_len;
+		break;
+	case URL_NONE:
+		return NULL;
+	}
+
+	return xstrndup(info->url + offset, length);
+}
+
+int cmd_url_parse(int argc,
+		  const char **argv,
+		  const char *prefix,
+		  struct repository *repo UNUSED)
+{
+	struct url_info info;
+	enum url_component selected = URL_NONE;
+	char *extracted;
+	int i;
+
+	argc = parse_options(argc, argv, prefix, builtin_url_parse_options,
+			     builtin_url_parse_usage, 0);
+
+	if (argc == 0)
+		usage_with_options(builtin_url_parse_usage,
+				   builtin_url_parse_options);
+
+	if (component_arg)
+		selected = get_component_or_die(component_arg);
+
+	for (i = 0; i < argc; i++) {
+		parse_or_die(argv[i], &info);
+
+		if (selected != URL_NONE) {
+			extracted = extract_component(selected, &info);
+			if (extracted) {
+				puts(extracted);
+				free(extracted);
+			}
+		}
+
+		free(info.url);
+	}
+
+	return 0;
+}
diff --git a/builtin/worktree.c b/builtin/worktree.c
index 4fd6f75..d21c43f 100644
--- a/builtin/worktree.c
+++ b/builtin/worktree.c
@@ -609,7 +609,7 @@ static int add_worktree(const char *path, const char *refname,
 	 * is_junk is cleared, but do return appropriate code when hook fails.
 	 */
 	if (!ret && opts->checkout && !opts->orphan) {
-		struct run_hooks_opt opt = RUN_HOOKS_OPT_INIT;
+		struct run_hooks_opt opt = RUN_HOOKS_OPT_INIT_FORCE_SERIAL;
 
 		strvec_pushl(&opt.env, "GIT_DIR", "GIT_WORK_TREE", NULL);
 		strvec_pushl(&opt.args,
diff --git a/cache-tree.c b/cache-tree.c
index 60bcc07..184f7e2 100644
--- a/cache-tree.c
+++ b/cache-tree.c
@@ -10,6 +10,7 @@
 #include "cache-tree.h"
 #include "object-file.h"
 #include "odb.h"
+#include "odb/transaction.h"
 #include "read-cache-ll.h"
 #include "replace-object.h"
 #include "repository.h"
@@ -238,8 +239,8 @@ int cache_tree_fully_valid(struct cache_tree *it)
 	if (!it)
 		return 0;
 	if (it->entry_count < 0 ||
-	    odb_has_object(the_repository->objects, &it->oid,
-			   HAS_OBJECT_RECHECK_PACKED | HAS_OBJECT_FETCH_PROMISOR))
+	    !odb_has_object(the_repository->objects, &it->oid,
+			    ODB_HAS_OBJECT_RECHECK_PACKED | ODB_HAS_OBJECT_FETCH_PROMISOR))
 		return 0;
 	for (i = 0; i < it->subtree_nr; i++) {
 		if (!cache_tree_fully_valid(it->down[i]->cache_tree))
@@ -292,7 +293,7 @@ static int update_one(struct cache_tree *it,
 
 	if (0 <= it->entry_count &&
 	    odb_has_object(the_repository->objects, &it->oid,
-			   HAS_OBJECT_RECHECK_PACKED | HAS_OBJECT_FETCH_PROMISOR))
+			   ODB_HAS_OBJECT_RECHECK_PACKED | ODB_HAS_OBJECT_FETCH_PROMISOR))
 		return it->entry_count;
 
 	/*
@@ -400,7 +401,7 @@ static int update_one(struct cache_tree *it,
 		if (is_null_oid(oid) ||
 		    (!ce_missing_ok &&
 		     !odb_has_object(the_repository->objects, oid,
-				     HAS_OBJECT_RECHECK_PACKED | HAS_OBJECT_FETCH_PROMISOR))) {
+				     ODB_HAS_OBJECT_RECHECK_PACKED | ODB_HAS_OBJECT_FETCH_PROMISOR))) {
 			strbuf_release(&buffer);
 			if (expected_missing)
 				return -1;
@@ -448,7 +449,7 @@ static int update_one(struct cache_tree *it,
 		struct object_id oid;
 		hash_object_file(the_hash_algo, buffer.buf, buffer.len,
 				 OBJ_TREE, &oid);
-		if (odb_has_object(the_repository->objects, &oid, HAS_OBJECT_RECHECK_PACKED))
+		if (odb_has_object(the_repository->objects, &oid, ODB_HAS_OBJECT_RECHECK_PACKED))
 			oidcpy(&it->oid, &oid);
 		else
 			to_invalidate = 1;
@@ -456,7 +457,7 @@ static int update_one(struct cache_tree *it,
 		hash_object_file(the_hash_algo, buffer.buf, buffer.len,
 				 OBJ_TREE, &it->oid);
 	} else if (odb_write_object_ext(the_repository->objects, buffer.buf, buffer.len, OBJ_TREE,
-					&it->oid, NULL, flags & WRITE_TREE_SILENT ? WRITE_OBJECT_SILENT : 0)) {
+					&it->oid, NULL, flags & WRITE_TREE_SILENT ? ODB_WRITE_OBJECT_SILENT : 0)) {
 		strbuf_release(&buffer);
 		return -1;
 	}
@@ -488,12 +489,12 @@ int cache_tree_update(struct index_state *istate, int flags)
 		prefetch_cache_entries(istate, must_check_existence);
 
 	trace_performance_enter();
-	trace2_region_enter("cache_tree", "update", the_repository);
+	trace2_region_enter("cache_tree", "update", istate->repo);
 	transaction = odb_transaction_begin(the_repository->objects);
 	i = update_one(istate->cache_tree, istate->cache, istate->cache_nr,
 		       "", 0, &skip, flags);
 	odb_transaction_commit(transaction);
-	trace2_region_leave("cache_tree", "update", the_repository);
+	trace2_region_leave("cache_tree", "update", istate->repo);
 	trace_performance_leave("cache_tree_update");
 	if (i < 0)
 		return i;
diff --git a/cbtree.c b/cbtree.c
index cf8cf75..8f5edbb 100644
--- a/cbtree.c
+++ b/cbtree.c
@@ -7,6 +7,11 @@
 #include "git-compat-util.h"
 #include "cbtree.h"
 
+static inline uint8_t *cb_node_key(struct cb_tree *t, struct cb_node *node)
+{
+	return (uint8_t *) node + t->key_offset;
+}
+
 static struct cb_node *cb_node_of(const void *p)
 {
 	return (struct cb_node *)((uintptr_t)p - 1);
@@ -33,6 +38,7 @@ struct cb_node *cb_insert(struct cb_tree *t, struct cb_node *node, size_t klen)
 	uint8_t c;
 	int newdirection;
 	struct cb_node **wherep, *p;
+	uint8_t *node_key, *p_key;
 
 	assert(!((uintptr_t)node & 1)); /* allocations must be aligned */
 
@@ -41,23 +47,26 @@ struct cb_node *cb_insert(struct cb_tree *t, struct cb_node *node, size_t klen)
 		return NULL;	/* success */
 	}
 
+	node_key = cb_node_key(t, node);
+
 	/* see if a node already exists */
-	p = cb_internal_best_match(t->root, node->k, klen);
+	p = cb_internal_best_match(t->root, node_key, klen);
+	p_key = cb_node_key(t, p);
 
 	/* find first differing byte */
 	for (newbyte = 0; newbyte < klen; newbyte++) {
-		if (p->k[newbyte] != node->k[newbyte])
+		if (p_key[newbyte] != node_key[newbyte])
 			goto different_byte_found;
 	}
 	return p;	/* element exists, let user deal with it */
 
 different_byte_found:
-	newotherbits = p->k[newbyte] ^ node->k[newbyte];
+	newotherbits = p_key[newbyte] ^ node_key[newbyte];
 	newotherbits |= newotherbits >> 1;
 	newotherbits |= newotherbits >> 2;
 	newotherbits |= newotherbits >> 4;
 	newotherbits = (newotherbits & ~(newotherbits >> 1)) ^ 255;
-	c = p->k[newbyte];
+	c = p_key[newbyte];
 	newdirection = (1 + (newotherbits | c)) >> 8;
 
 	node->byte = newbyte;
@@ -78,7 +87,7 @@ struct cb_node *cb_insert(struct cb_tree *t, struct cb_node *node, size_t klen)
 			break;
 		if (q->byte == newbyte && q->otherbits > newotherbits)
 			break;
-		c = q->byte < klen ? node->k[q->byte] : 0;
+		c = q->byte < klen ? node_key[q->byte] : 0;
 		direction = (1 + (q->otherbits | c)) >> 8;
 		wherep = q->child + direction;
 	}
@@ -93,29 +102,32 @@ struct cb_node *cb_lookup(struct cb_tree *t, const uint8_t *k, size_t klen)
 {
 	struct cb_node *p = cb_internal_best_match(t->root, k, klen);
 
-	return p && !memcmp(p->k, k, klen) ? p : NULL;
+	return p && !memcmp(cb_node_key(t, p), k, klen) ? p : NULL;
 }
 
-static enum cb_next cb_descend(struct cb_node *p, cb_iter fn, void *arg)
+static int cb_descend(struct cb_node *p, cb_iter fn, void *arg)
 {
 	if (1 & (uintptr_t)p) {
 		struct cb_node *q = cb_node_of(p);
-		enum cb_next n = cb_descend(q->child[0], fn, arg);
-
-		return n == CB_BREAK ? n : cb_descend(q->child[1], fn, arg);
+		int ret = cb_descend(q->child[0], fn, arg);
+		if (ret)
+			return ret;
+		return cb_descend(q->child[1], fn, arg);
 	} else {
 		return fn(p, arg);
 	}
 }
 
-void cb_each(struct cb_tree *t, const uint8_t *kpfx, size_t klen,
-			cb_iter fn, void *arg)
+int cb_each(struct cb_tree *t, const uint8_t *kpfx, size_t klen,
+	    cb_iter fn, void *arg)
 {
 	struct cb_node *p = t->root;
 	struct cb_node *top = p;
 	size_t i = 0;
+	uint8_t *p_key;
 
-	if (!p) return; /* empty tree */
+	if (!p)
+		return 0; /* empty tree */
 
 	/* Walk tree, maintaining top pointer */
 	while (1 & (uintptr_t)p) {
@@ -128,9 +140,11 @@ void cb_each(struct cb_tree *t, const uint8_t *kpfx, size_t klen,
 			top = p;
 	}
 
+	p_key = cb_node_key(t, p);
 	for (i = 0; i < klen; i++) {
-		if (p->k[i] != kpfx[i])
-			return; /* "best" match failed */
+		if (p_key[i] != kpfx[i])
+			return 0; /* "best" match failed */
 	}
-	cb_descend(top, fn, arg);
+
+	return cb_descend(top, fn, arg);
 }
diff --git a/cbtree.h b/cbtree.h
index 43193ab..4647d4a 100644
--- a/cbtree.h
+++ b/cbtree.h
@@ -6,9 +6,9 @@
  *
  * This is adapted to store arbitrary data (not just NUL-terminated C strings
  * and allocates no memory internally.  The user needs to allocate
- * "struct cb_node" and fill cb_node.k[] with arbitrary match data
- * for memcmp.
- * If "klen" is variable, then it should be embedded into "c_node.k[]"
+ * "struct cb_node" and provide `key_offset` to indicate where the key can be
+ * found relative to the `struct cb_node` for memcmp.
+ * If "klen" is variable, then it should be embedded into the key.
  * Recursion is bound by the maximum value of "klen" used.
  */
 #ifndef CBTREE_H
@@ -23,32 +23,34 @@ struct cb_node {
 	 */
 	uint32_t byte;
 	uint8_t otherbits;
-	uint8_t k[FLEX_ARRAY]; /* arbitrary data, unaligned */
 };
 
 struct cb_tree {
 	struct cb_node *root;
+	ptrdiff_t key_offset;
 };
 
-enum cb_next {
-	CB_CONTINUE = 0,
-	CB_BREAK = 1
-};
-
-#define CBTREE_INIT { 0 }
-
-static inline void cb_init(struct cb_tree *t)
+static inline void cb_init(struct cb_tree *t,
+			   ptrdiff_t key_offset)
 {
-	struct cb_tree blank = CBTREE_INIT;
+	struct cb_tree blank = {
+		.key_offset = key_offset,
+	};
 	memcpy(t, &blank, sizeof(*t));
 }
 
 struct cb_node *cb_lookup(struct cb_tree *, const uint8_t *k, size_t klen);
 struct cb_node *cb_insert(struct cb_tree *, struct cb_node *, size_t klen);
 
-typedef enum cb_next (*cb_iter)(struct cb_node *, void *arg);
+/*
+ * Callback invoked by `cb_each()` for each node in the critbit tree. A return
+ * value of 0 will cause the iteration to continue, a non-zero return code will
+ * cause iteration to abort. The error code will be relayed back from
+ * `cb_each()` in that case.
+ */
+typedef int (*cb_iter)(struct cb_node *, void *arg);
 
-void cb_each(struct cb_tree *, const uint8_t *kpfx, size_t klen,
-		cb_iter, void *arg);
+int cb_each(struct cb_tree *, const uint8_t *kpfx, size_t klen,
+	    cb_iter, void *arg);
 
 #endif /* CBTREE_H */
diff --git a/ci/install-dependencies.sh b/ci/install-dependencies.sh
index c55441d..10c3530 100755
--- a/ci/install-dependencies.sh
+++ b/ci/install-dependencies.sh
@@ -29,7 +29,7 @@
 	apk add --update shadow sudo meson ninja-build gcc libc-dev curl-dev openssl-dev expat-dev gettext \
 		zlib-ng-dev 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
+		bash cvs gnupg perl-cgi perl-dbd-sqlite perl-io-tty cargo >/dev/null
 	;;
 fedora-*|almalinux-*)
 	case "$jobname" in
diff --git a/ci/lib.sh b/ci/lib.sh
index 42a2b6a..6e3799c 100755
--- a/ci/lib.sh
+++ b/ci/lib.sh
@@ -314,6 +314,17 @@
 export GIT_TEST_CLONE_2GB=true
 export SKIP_DASHED_BUILT_INS=YesPlease
 
+# In order to give maximum test coverage to contributor builds,
+# preferrably even before the changes consume public review bandwidth,
+# enable "expensive" tests for PR events.
+# In order to catch bugs introduced at integration time by mismerges,
+# enable the long tests for pushes to the integration branches as well.
+case "$GITHUB_EVENT_NAME,$CI_BRANCH" in
+pull_request,*|push,*next*|push,*master*|push,*main*|push,*maint*)
+	export GIT_TEST_LONG=YesPlease
+	;;
+esac
+
 case "$distro" in
 ubuntu-*)
 	# Python 2 is end of life, and Ubuntu 23.04 and newer don't actually
@@ -372,6 +383,9 @@
 osx-meson)
 	MESONFLAGS="$MESONFLAGS -Dcredential_helpers=osxkeychain"
 	;;
+windows-*)
+	export NO_RUST=UnfortunatelyYes
+	;;
 esac
 
 MAKEFLAGS="$MAKEFLAGS CC=${CC:-cc}"
diff --git a/ci/run-build-and-tests.sh b/ci/run-build-and-tests.sh
index 28cfe73..1d9a0a7 100755
--- a/ci/run-build-and-tests.sh
+++ b/ci/run-build-and-tests.sh
@@ -8,11 +8,19 @@
 export TEST_CONTRIB_TOO=yes
 
 case "$jobname" in
+linux-musl-meson)
+	MESONFLAGS="$MESONFLAGS -Drust=disabled"
+	export GIT_TEST_USE_SET_E=yes
+	;;
+almalinux-*|debian-*|fedora-*|linux-*)
+	export GIT_TEST_USE_SET_E=yes
+	;;
+esac
+
+case "$jobname" in
 fedora-breaking-changes-musl|linux-breaking-changes)
 	export WITH_BREAKING_CHANGES=YesPlease
-	export WITH_RUST=YesPlease
 	MESONFLAGS="$MESONFLAGS -Dbreaking_changes=true"
-	MESONFLAGS="$MESONFLAGS -Drust=enabled"
 	;;
 linux-TEST-vars)
 	export OPENSSL_SHA1_UNSAFE=YesPlease
@@ -30,6 +38,7 @@
 	export GIT_TEST_PACK_USE_BITMAP_BOUNDARY_TRAVERSAL=1
 	;;
 linux-clang)
+	export NO_RUST=UnfortunatelyYes
 	export GIT_TEST_DEFAULT_HASH=sha1
 	;;
 linux-sha256)
diff --git a/command-list.txt b/command-list.txt
index f9005cf..21b802c 100644
--- a/command-list.txt
+++ b/command-list.txt
@@ -108,6 +108,7 @@
 git-for-each-ref                        plumbinginterrogators
 git-for-each-repo                       plumbinginterrogators
 git-format-patch                        mainporcelain
+git-format-rev                          plumbinginterrogators
 git-fsck                                ancillaryinterrogators          complete
 git-gc                                  mainporcelain
 git-get-tar-commit-id                   plumbinginterrogators
@@ -202,6 +203,7 @@
 git-update-server-info                  synchingrepositories
 git-upload-archive                      synchelpers
 git-upload-pack                         synchelpers
+git-url-parse                           purehelpers
 git-var                                 plumbinginterrogators
 git-verify-commit                       ancillaryinterrogators
 git-verify-pack                         plumbinginterrogators
diff --git a/commit-graph.c b/commit-graph.c
index c030003..9abe62b 100644
--- a/commit-graph.c
+++ b/commit-graph.c
@@ -1319,6 +1319,37 @@ static int write_graph_chunk_data(struct hashfile *f,
 	return 0;
 }
 
+/*
+ * Compute the generation offset between the commit date and its generation.
+ * This is what's ultimately stored as generation number in the commit graph.
+ *
+ * Note that the computation of the commit date is more involved than you might
+ * think. Instead of using the full commit date, we're in fact masking bits so
+ * that only the 34 lowest bits are considered. This results from the fact that
+ * commit graphs themselves only ever store 34 bits of the commit date
+ * themselves.
+ *
+ * This means that if we have a commit date that exceeds 34 bits we'll end up
+ * in situations where depending on whether the commit has been parsed from the
+ * object database or the commit graph we'll have different dates, where the
+ * ones parsed from the object database would have full 64 bit precision.
+ *
+ * But ultimately, we only ever want the offset to be relative to what we
+ * actually end up storing on disk, and hence we have to mask all the other
+ * bits.
+ */
+static timestamp_t compute_generation_offset(struct commit *c)
+{
+	timestamp_t masked_date;
+
+	if (sizeof(timestamp_t) > 4)
+		masked_date = c->date & (((timestamp_t) 1 << 34) - 1);
+	else
+		masked_date = c->date;
+
+	return commit_graph_data_at(c)->generation - masked_date;
+}
+
 static int write_graph_chunk_generation_data(struct hashfile *f,
 					     void *data)
 {
@@ -1329,7 +1360,7 @@ static int write_graph_chunk_generation_data(struct hashfile *f,
 		struct commit *c = ctx->commits.items[i];
 		timestamp_t offset;
 		repo_parse_commit(ctx->r, c);
-		offset = commit_graph_data_at(c)->generation - c->date;
+		offset = compute_generation_offset(c);
 		display_progress(ctx->progress, ++ctx->progress_cnt);
 
 		if (offset > GENERATION_NUMBER_V2_OFFSET_MAX) {
@@ -1350,7 +1381,7 @@ static int write_graph_chunk_generation_data_overflow(struct hashfile *f,
 	int i;
 	for (i = 0; i < ctx->commits.nr; i++) {
 		struct commit *c = ctx->commits.items[i];
-		timestamp_t offset = commit_graph_data_at(c)->generation - c->date;
+		timestamp_t offset = compute_generation_offset(c);
 		display_progress(ctx->progress, ++ctx->progress_cnt);
 
 		if (offset > GENERATION_NUMBER_V2_OFFSET_MAX) {
@@ -1741,7 +1772,7 @@ static void compute_generation_numbers(struct write_commit_graph_context *ctx)
 
 	for (i = 0; i < ctx->commits.nr; i++) {
 		struct commit *c = ctx->commits.items[i];
-		timestamp_t offset = commit_graph_data_at(c)->generation - c->date;
+		timestamp_t offset = compute_generation_offset(c);
 		if (offset > GENERATION_NUMBER_V2_OFFSET_MAX)
 			ctx->num_generation_data_overflows++;
 	}
@@ -1969,6 +2000,9 @@ static void fill_oids_from_all_packs(struct write_commit_graph_context *ctx)
 {
 	struct odb_source *source;
 	enum object_type type;
+	struct odb_for_each_object_options opts = {
+		.flags = ODB_FOR_EACH_OBJECT_PACK_ORDER,
+	};
 	struct object_info oi = {
 		.typep = &type,
 	};
@@ -1983,7 +2017,7 @@ static void fill_oids_from_all_packs(struct write_commit_graph_context *ctx)
 	for (source = ctx->r->objects->sources; source; source = source->next) {
 		struct odb_source_files *files = odb_source_files_downcast(source);
 		packfile_store_for_each_object(files->packed, &oi, add_packed_commits_oi,
-					       ctx, ODB_FOR_EACH_OBJECT_PACK_ORDER);
+					       ctx, &opts);
 	}
 
 	if (ctx->progress_done < ctx->approx_nr_objects)
diff --git a/commit-reach.c b/commit-reach.c
index d3a9b3e..5df471a 100644
--- a/commit-reach.c
+++ b/commit-reach.c
@@ -17,8 +17,9 @@
 #define PARENT2		(1u<<17)
 #define STALE		(1u<<18)
 #define RESULT		(1u<<19)
+#define ENQUEUED	(1u<<20)
 
-static const unsigned all_flags = (PARENT1 | PARENT2 | STALE | RESULT);
+static const unsigned all_flags = (PARENT1 | PARENT2 | STALE | RESULT | ENQUEUED);
 
 static int compare_commits_by_gen(const void *_a, const void *_b)
 {
@@ -39,14 +40,60 @@ static int compare_commits_by_gen(const void *_a, const void *_b)
 	return 0;
 }
 
-static int queue_has_nonstale(struct prio_queue *queue)
+/*
+ * A prio_queue with O(1) termination check.  'max_nonstale' tracks
+ * the lowest-priority non-stale commit enqueued so far; once it is
+ * popped, every remaining entry is known to be STALE.
+ */
+struct nonstale_queue {
+	struct prio_queue pq;
+	struct commit *max_nonstale;
+};
+
+static void nonstale_queue_put(struct nonstale_queue *queue,
+			       struct commit *c)
 {
-	for (size_t i = 0; i < queue->nr; i++) {
-		struct commit *commit = queue->array[i].data;
-		if (!(commit->object.flags & STALE))
-			return 1;
-	}
-	return 0;
+	struct commit *old = queue->max_nonstale;
+
+	prio_queue_put(&queue->pq, c);
+	if (c->object.flags & STALE)
+		return;
+	if (!old || queue->pq.compare(old, c, queue->pq.cb_data) <= 0)
+		queue->max_nonstale = c;
+}
+
+static struct commit *nonstale_queue_get(struct nonstale_queue *queue)
+{
+	struct commit *commit = prio_queue_get(&queue->pq);
+
+	if (commit == queue->max_nonstale)
+		queue->max_nonstale = NULL;
+
+	return commit;
+}
+
+static void clear_nonstale_queue(struct nonstale_queue *queue)
+{
+	clear_prio_queue(&queue->pq);
+	queue->max_nonstale = NULL;
+}
+
+static void nonstale_queue_put_dedup(struct nonstale_queue *queue,
+				     struct commit *c)
+{
+	if (c->object.flags & ENQUEUED)
+		return;
+	c->object.flags |= ENQUEUED;
+	nonstale_queue_put(queue, c);
+}
+
+static struct commit *nonstale_queue_get_dedup(struct nonstale_queue *queue)
+{
+	struct commit *commit = nonstale_queue_get(queue);
+
+	if (commit)
+		commit->object.flags &= ~ENQUEUED;
+	return commit;
 }
 
 /* all input commits in one and twos[] must have been parsed! */
@@ -54,31 +101,33 @@ static int paint_down_to_common(struct repository *r,
 				struct commit *one, int n,
 				struct commit **twos,
 				timestamp_t min_generation,
-				int ignore_missing_commits,
+				enum merge_base_flags mb_flags,
 				struct commit_list **result)
 {
-	struct prio_queue queue = { compare_commits_by_gen_then_commit_date };
+	struct nonstale_queue queue = {
+		{ compare_commits_by_gen_then_commit_date }
+	};
 	int i;
 	timestamp_t last_gen = GENERATION_NUMBER_INFINITY;
 	struct commit_list **tail = result;
 
 	if (!min_generation && !corrected_commit_dates_enabled(r))
-		queue.compare = compare_commits_by_commit_date;
+		queue.pq.compare = compare_commits_by_commit_date;
 
 	one->object.flags |= PARENT1;
 	if (!n) {
 		commit_list_append(one, result);
 		return 0;
 	}
-	prio_queue_put(&queue, one);
+	nonstale_queue_put_dedup(&queue, one);
 
 	for (i = 0; i < n; i++) {
 		twos[i]->object.flags |= PARENT2;
-		prio_queue_put(&queue, twos[i]);
+		nonstale_queue_put_dedup(&queue, twos[i]);
 	}
 
-	while (queue_has_nonstale(&queue)) {
-		struct commit *commit = prio_queue_get(&queue);
+	while (queue.max_nonstale) {
+		struct commit *commit = nonstale_queue_get_dedup(&queue);
 		struct commit_list *parents;
 		int flags;
 		timestamp_t generation = commit_graph_generation(commit);
@@ -97,6 +146,14 @@ static int paint_down_to_common(struct repository *r,
 			if (!(commit->object.flags & RESULT)) {
 				commit->object.flags |= RESULT;
 				tail = commit_list_append(commit, tail);
+				/*
+				 * The queue is generation-ordered; no
+				 * remaining common ancestor can be a
+				 * descendant of this one.
+				 */
+				if (!(mb_flags & MERGE_BASE_FIND_ALL) &&
+				    generation < GENERATION_NUMBER_INFINITY)
+					break;
 			}
 			/* Mark parents of a found merge stale */
 			flags |= STALE;
@@ -108,7 +165,7 @@ static int paint_down_to_common(struct repository *r,
 			if ((p->object.flags & flags) == flags)
 				continue;
 			if (repo_parse_commit(r, p)) {
-				clear_prio_queue(&queue);
+				clear_nonstale_queue(&queue);
 				commit_list_free(*result);
 				*result = NULL;
 				/*
@@ -118,17 +175,17 @@ static int paint_down_to_common(struct repository *r,
 				 * corrupt commits would already have been
 				 * dispatched with a `die()`.
 				 */
-				if (ignore_missing_commits)
+				if (mb_flags & MERGE_BASE_IGNORE_MISSING_COMMITS)
 					return 0;
 				return error(_("could not parse commit %s"),
 					     oid_to_hex(&p->object.oid));
 			}
 			p->object.flags |= flags;
-			prio_queue_put(&queue, p);
+			nonstale_queue_put_dedup(&queue, p);
 		}
 	}
 
-	clear_prio_queue(&queue);
+	clear_nonstale_queue(&queue);
 	commit_list_sort_by_date(result);
 	return 0;
 }
@@ -136,6 +193,7 @@ static int paint_down_to_common(struct repository *r,
 static int merge_bases_many(struct repository *r,
 			    struct commit *one, int n,
 			    struct commit **twos,
+			    enum merge_base_flags mb_flags,
 			    struct commit_list **result)
 {
 	struct commit_list *list = NULL, **tail = result;
@@ -165,7 +223,7 @@ static int merge_bases_many(struct repository *r,
 				     oid_to_hex(&twos[i]->object.oid));
 	}
 
-	if (paint_down_to_common(r, one, n, twos, 0, 0, &list)) {
+	if (paint_down_to_common(r, one, n, twos, 0, mb_flags, &list)) {
 		commit_list_free(list);
 		return -1;
 	}
@@ -246,7 +304,8 @@ static int remove_redundant_no_gen(struct repository *r,
 				min_generation = curr_generation;
 		}
 		if (paint_down_to_common(r, array[i], filled,
-					 work, min_generation, 0, &common)) {
+					 work, min_generation,
+					 MERGE_BASE_FIND_ALL, &common)) {
 			clear_commit_marks(array[i], all_flags);
 			clear_commit_marks_many(filled, work, all_flags);
 			commit_list_free(common);
@@ -425,6 +484,7 @@ static int get_merge_bases_many_0(struct repository *r,
 				  size_t n,
 				  struct commit **twos,
 				  int cleanup,
+				  enum merge_base_flags mb_flags,
 				  struct commit_list **result)
 {
 	struct commit_list *list, **tail = result;
@@ -432,7 +492,7 @@ static int get_merge_bases_many_0(struct repository *r,
 	size_t cnt, i;
 	int ret;
 
-	if (merge_bases_many(r, one, n, twos, result) < 0)
+	if (merge_bases_many(r, one, n, twos, mb_flags, result) < 0)
 		return -1;
 	for (i = 0; i < n; i++) {
 		if (one == twos[i])
@@ -475,16 +535,18 @@ int repo_get_merge_bases_many(struct repository *r,
 			      struct commit **twos,
 			      struct commit_list **result)
 {
-	return get_merge_bases_many_0(r, one, n, twos, 1, result);
+	return get_merge_bases_many_0(r, one, n, twos, 1,
+				     MERGE_BASE_FIND_ALL, result);
 }
 
 int repo_get_merge_bases_many_dirty(struct repository *r,
 				    struct commit *one,
 				    size_t n,
 				    struct commit **twos,
+				    enum merge_base_flags mb_flags,
 				    struct commit_list **result)
 {
-	return get_merge_bases_many_0(r, one, n, twos, 0, result);
+	return get_merge_bases_many_0(r, one, n, twos, 0, mb_flags, result);
 }
 
 int repo_get_merge_bases(struct repository *r,
@@ -492,7 +554,8 @@ int repo_get_merge_bases(struct repository *r,
 			 struct commit *two,
 			 struct commit_list **result)
 {
-	return get_merge_bases_many_0(r, one, 1, &two, 1, result);
+	return get_merge_bases_many_0(r, one, 1, &two, 1,
+				     MERGE_BASE_FIND_ALL, result);
 }
 
 /*
@@ -537,6 +600,10 @@ int repo_in_merge_bases_many(struct repository *r, struct commit *commit,
 	struct commit_list *bases = NULL;
 	int ret = 0, i;
 	timestamp_t generation, max_generation = GENERATION_NUMBER_ZERO;
+	enum merge_base_flags mb_flags = MERGE_BASE_FIND_ALL;
+
+	if (ignore_missing_commits)
+		mb_flags |= MERGE_BASE_IGNORE_MISSING_COMMITS;
 
 	if (repo_parse_commit(r, commit))
 		return ignore_missing_commits ? 0 : -1;
@@ -555,7 +622,7 @@ int repo_in_merge_bases_many(struct repository *r, struct commit *commit,
 
 	if (paint_down_to_common(r, commit,
 				 nr_reference, reference,
-				 generation, ignore_missing_commits, &bases))
+				 generation, mb_flags, &bases))
 		ret = -1;
 	else if (commit->object.flags & PARENT2)
 		ret = 1;
@@ -1022,11 +1089,11 @@ struct commit_list *get_reachable_subset(struct commit **from, size_t nr_from,
 define_commit_slab(bit_arrays, struct bitmap *);
 static struct bit_arrays bit_arrays;
 
-static void insert_no_dup(struct prio_queue *queue, struct commit *c)
+static void insert_no_dup(struct nonstale_queue *queue, struct commit *c)
 {
 	if (c->object.flags & PARENT2)
 		return;
-	prio_queue_put(queue, c);
+	nonstale_queue_put(queue, c);
 	c->object.flags |= PARENT2;
 }
 
@@ -1051,7 +1118,9 @@ void ahead_behind(struct repository *r,
 		  struct commit **commits, size_t commits_nr,
 		  struct ahead_behind_count *counts, size_t counts_nr)
 {
-	struct prio_queue queue = { .compare = compare_commits_by_gen_then_commit_date };
+	struct nonstale_queue queue = {
+		{ .compare = compare_commits_by_gen_then_commit_date }
+	};
 	size_t width = DIV_ROUND_UP(commits_nr, BITS_IN_EWORD);
 
 	if (!commits_nr || !counts_nr)
@@ -1074,8 +1143,8 @@ void ahead_behind(struct repository *r,
 		insert_no_dup(&queue, c);
 	}
 
-	while (queue_has_nonstale(&queue)) {
-		struct commit *c = prio_queue_get(&queue);
+	while (queue.max_nonstale) {
+		struct commit *c = nonstale_queue_get(&queue);
 		struct commit_list *p;
 		struct bitmap *bitmap_c = get_bit_array(c, width);
 
@@ -1117,15 +1186,14 @@ void ahead_behind(struct repository *r,
 
 	/* STALE is used here, PARENT2 is used by insert_no_dup(). */
 	repo_clear_commit_marks(r, PARENT2 | STALE);
-	for (size_t i = 0; i < queue.nr; i++)
-		free_bit_array(queue.array[i].data);
+	for (size_t i = 0; i < queue.pq.nr; i++)
+		free_bit_array(queue.pq.array[i].data);
 	clear_bit_arrays(&bit_arrays);
-	clear_prio_queue(&queue);
+	clear_nonstale_queue(&queue);
 }
 
 struct commit_and_index {
 	struct commit *commit;
-	unsigned int index;
 	timestamp_t generation;
 };
 
@@ -1165,7 +1233,6 @@ void tips_reachable_from_bases(struct repository *r,
 
 	for (size_t i = 0; i < tips_nr; i++) {
 		commits[i].commit = tips[i];
-		commits[i].index = i;
 		commits[i].generation = commit_graph_generation(tips[i]);
 	}
 
@@ -1173,6 +1240,9 @@ void tips_reachable_from_bases(struct repository *r,
 	QSORT(commits, tips_nr, compare_commit_and_index_by_generation);
 	min_generation = commits[0].generation;
 
+	for (size_t i = 0; i < tips_nr; i++)
+		commits[i].commit->object.flags |= RESULT;
+
 	while (bases) {
 		repo_parse_commit(r, bases->item);
 		commit_list_insert(bases->item, &stack);
@@ -1183,20 +1253,16 @@ void tips_reachable_from_bases(struct repository *r,
 		int explored_all_parents = 1;
 		struct commit_list *p;
 		struct commit *c = stack->item;
-		timestamp_t c_gen = commit_graph_generation(c);
 
 		/* Does it match any of our tips? */
-		for (size_t j = min_generation_index; j < tips_nr; j++) {
-			if (c_gen < commits[j].generation)
-				break;
+		{
+			if (c->object.flags & RESULT) {
+				c->object.flags |= mark;
 
-			if (commits[j].commit == c) {
-				tips[commits[j].index]->object.flags |= mark;
-
-				if (j == min_generation_index) {
-					unsigned int k = j + 1;
+				if (commits[min_generation_index].commit->object.flags & mark) {
+					unsigned int k = min_generation_index + 1;
 					while (k < tips_nr &&
-					       (tips[commits[k].index]->object.flags & mark))
+					       (commits[k].commit->object.flags & mark))
 						k++;
 
 					/* Terminate early if all found. */
@@ -1232,6 +1298,8 @@ void tips_reachable_from_bases(struct repository *r,
 	}
 
 done:
+	for (size_t i = 0; i < tips_nr; i++)
+		commits[i].commit->object.flags &= ~RESULT;
 	free(commits);
 	repo_clear_commit_marks(r, SEEN);
 	commit_list_free(stack);
diff --git a/commit-reach.h b/commit-reach.h
index 6012402..3f3a563 100644
--- a/commit-reach.h
+++ b/commit-reach.h
@@ -17,10 +17,20 @@ int repo_get_merge_bases_many(struct repository *r,
 			      struct commit *one, size_t n,
 			      struct commit **twos,
 			      struct commit_list **result);
-/* To be used only when object flags after this call no longer matter */
+enum merge_base_flags {
+	MERGE_BASE_IGNORE_MISSING_COMMITS = (1 << 0),
+	MERGE_BASE_FIND_ALL               = (1 << 1),
+};
+
+/*
+ * To be used only when object flags after this call no longer matter.
+ * Without MERGE_BASE_FIND_ALL and with generation numbers available,
+ * returns after finding the first merge-base, skipping the STALE drain.
+ */
 int repo_get_merge_bases_many_dirty(struct repository *r,
 				    struct commit *one, size_t n,
 				    struct commit **twos,
+				    enum merge_base_flags mb_flags,
 				    struct commit_list **result);
 
 int get_octopus_merge_bases(struct commit_list *in, struct commit_list **result);
diff --git a/commit.c b/commit.c
index 80d8d07..fd87235 100644
--- a/commit.c
+++ b/commit.c
@@ -434,6 +434,27 @@ static inline void set_commit_tree(struct commit *c, struct tree *t)
 	c->maybe_tree = t;
 }
 
+static void load_tree_from_commit_contents(struct repository *r, struct commit *commit)
+{
+	enum object_type type;
+	unsigned long size;
+	char *buf;
+	const char *p;
+	struct object_id tree_oid;
+
+	buf = odb_read_object(r->objects, &commit->object.oid, &type, &size);
+	if (!buf)
+		return;
+
+	if (type == OBJ_COMMIT &&
+	    skip_prefix(buf, "tree ", &p) &&
+	    !parse_oid_hex_algop(p, &tree_oid, &p, r->hash_algo) &&
+	    *p == '\n')
+		set_commit_tree(commit, lookup_tree(r, &tree_oid));
+
+	free(buf);
+}
+
 struct tree *repo_get_commit_tree(struct repository *r,
 				  const struct commit *commit)
 {
@@ -443,7 +464,17 @@ struct tree *repo_get_commit_tree(struct repository *r,
 	if (commit_graph_position(commit) != COMMIT_NOT_FROM_GRAPH)
 		return get_commit_tree_in_graph(r, commit);
 
-	return NULL;
+	/*
+	 * This is either a corrupt commit, or one which we partially loaded
+	 * from a graph file but then subsequently threw away the graph data.
+	 *
+	 * Optimistically assume it's the latter and try to reload from
+	 * scratch. This gives a performance penalty if it really is a corrupt
+	 * commit, but presumably that happens rarely (and only once per
+	 * process).
+	 */
+	load_tree_from_commit_contents(r, (struct commit *)commit);
+	return commit->maybe_tree;
 }
 
 struct object_id *get_commit_tree_oid(const struct commit *commit)
@@ -1558,16 +1589,16 @@ int commit_tree(const char *msg, size_t msg_len, const struct object_id *tree,
 	return result;
 }
 
-static int find_invalid_utf8(const char *buf, int len)
+static bool has_invalid_utf8(const char *buf, size_t len, size_t *bad_offset)
 {
-	int offset = 0;
+	size_t offset = 0;
 	static const unsigned int max_codepoint[] = {
 		0x7f, 0x7ff, 0xffff, 0x10ffff
 	};
 
 	while (len) {
 		unsigned char c = *buf++;
-		int bytes, bad_offset;
+		unsigned bytes;
 		unsigned int codepoint;
 		unsigned int min_val, max_val;
 
@@ -1578,7 +1609,7 @@ static int find_invalid_utf8(const char *buf, int len)
 		if (c < 0x80)
 			continue;
 
-		bad_offset = offset-1;
+		*bad_offset = offset-1;
 
 		/*
 		 * Count how many more high bits set: that's how
@@ -1595,11 +1626,11 @@ static int find_invalid_utf8(const char *buf, int len)
 		 * codepoints beyond U+10FFFF, which are guaranteed never to exist.
 		 */
 		if (bytes < 1 || 3 < bytes)
-			return bad_offset;
+			return true;
 
 		/* Do we *have* that many bytes? */
 		if (len < bytes)
-			return bad_offset;
+			return true;
 
 		/*
 		 * Place the encoded bits at the bottom of the value and compute the
@@ -1617,43 +1648,42 @@ static int find_invalid_utf8(const char *buf, int len)
 			codepoint <<= 6;
 			codepoint |= *buf & 0x3f;
 			if ((*buf++ & 0xc0) != 0x80)
-				return bad_offset;
+				return true;
 		} while (--bytes);
 
 		/* Reject codepoints that are out of range for the sequence length. */
 		if (codepoint < min_val || codepoint > max_val)
-			return bad_offset;
+			return true;
 		/* Surrogates are only for UTF-16 and cannot be encoded in UTF-8. */
 		if ((codepoint & 0x1ff800) == 0xd800)
-			return bad_offset;
+			return true;
 		/* U+xxFFFE and U+xxFFFF are guaranteed non-characters. */
 		if ((codepoint & 0xfffe) == 0xfffe)
-			return bad_offset;
+			return true;
 		/* So are anything in the range U+FDD0..U+FDEF. */
 		if (codepoint >= 0xfdd0 && codepoint <= 0xfdef)
-			return bad_offset;
+			return true;
 	}
-	return -1;
+	return false;
 }
 
 /*
- * This verifies that the buffer is in proper utf8 format.
+ * This ensures that the buffer is in proper utf8 format.
  *
  * If it isn't, it assumes any non-utf8 characters are Latin1,
  * and does the conversion.
  */
-static int verify_utf8(struct strbuf *buf)
+static int ensure_utf8(struct strbuf *buf)
 {
 	int ok = 1;
-	long pos = 0;
+	size_t pos = 0;
 
 	for (;;) {
-		int bad;
+		size_t bad;
 		unsigned char c;
 		unsigned char replace[2];
 
-		bad = find_invalid_utf8(buf->buf + pos, buf->len - pos);
-		if (bad < 0)
+		if (!has_invalid_utf8(buf->buf + pos, buf->len - pos, &bad))
 			return ok;
 		pos += bad;
 		ok = 0;
@@ -1726,6 +1756,7 @@ int commit_tree_extended(const char *msg, size_t msg_len,
 	struct repository *r = the_repository;
 	int result = 0;
 	int encoding_is_utf8;
+	bool warned = false;
 	struct strbuf buffer = STRBUF_INIT, compat_buffer = STRBUF_INIT;
 	struct strbuf sig = STRBUF_INIT, compat_sig = STRBUF_INIT;
 	struct object_id *parent_buf = NULL, *compat_oid = NULL;
@@ -1747,6 +1778,13 @@ int commit_tree_extended(const char *msg, size_t msg_len,
 		oidcpy(&parent_buf[i++], &p->item->object.oid);
 
 	write_commit_tree(&buffer, msg, msg_len, tree, parent_buf, nparents, author, committer, extra);
+
+	/* And check the encoding. */
+	if (encoding_is_utf8 && !ensure_utf8(&buffer)) {
+		fprintf(stderr, _(commit_utf8_warn));
+		warned = true;
+	}
+
 	if (sign_commit && sign_buffer(&buffer, &sig, sign_commit,
 				       SIGN_BUFFER_USE_DEFAULT_KEY)) {
 		result = -1;
@@ -1780,6 +1818,9 @@ int commit_tree_extended(const char *msg, size_t msg_len,
 		free_commit_extra_headers(compat_extra);
 		free(mapped_parents);
 
+		if (encoding_is_utf8 && !ensure_utf8(&compat_buffer) && !warned)
+			fprintf(stderr, _(commit_utf8_warn));
+
 		if (sign_commit && sign_buffer(&compat_buffer, &compat_sig,
 					       sign_commit,
 					       SIGN_BUFFER_USE_DEFAULT_KEY)) {
@@ -1818,10 +1859,6 @@ int commit_tree_extended(const char *msg, size_t msg_len,
 		}
 	}
 
-	/* And check the encoding. */
-	if (encoding_is_utf8 && (!verify_utf8(&buffer) || !verify_utf8(&compat_buffer)))
-		fprintf(stderr, _(commit_utf8_warn));
-
 	if (r->compat_hash_algo) {
 		hash_object_file(r->compat_hash_algo, compat_buffer.buf, compat_buffer.len,
 			OBJ_COMMIT, &compat_oid_buf);
@@ -1970,7 +2007,7 @@ size_t ignored_log_message_bytes(const char *buf, size_t len)
 int run_commit_hook(int editor_is_used, const char *index_file,
 		    int *invoked_hook, const char *name, ...)
 {
-	struct run_hooks_opt opt = RUN_HOOKS_OPT_INIT;
+	struct run_hooks_opt opt = RUN_HOOKS_OPT_INIT_FORCE_SERIAL;
 	va_list args;
 	const char *arg;
 
diff --git a/commit.h b/commit.h
index 5815004..5352056 100644
--- a/commit.h
+++ b/commit.h
@@ -203,25 +203,6 @@ struct commit_list *commit_list_reverse(struct commit_list *list);
 
 void commit_list_free(struct commit_list *list);
 
-/*
- * Deprecated compatibility functions for `struct commit_list`, to be removed
- * once Git 2.53 is released.
- */
-static inline struct commit_list *copy_commit_list(struct commit_list *l)
-{
-	return commit_list_copy(l);
-}
-
-static inline struct commit_list *reverse_commit_list(struct commit_list *l)
-{
-	return commit_list_reverse(l);
-}
-
-static inline void free_commit_list(struct commit_list *l)
-{
-	commit_list_free(l);
-}
-
 struct rev_info; /* in revision.h, it circularly uses enum cmit_fmt */
 
 const char *repo_logmsg_reencode(struct repository *r,
diff --git a/compat/fsmonitor/fsm-health-linux.c b/compat/fsmonitor/fsm-health-linux.c
new file mode 100644
index 0000000..43d67c4
--- /dev/null
+++ b/compat/fsmonitor/fsm-health-linux.c
@@ -0,0 +1,33 @@
+#include "git-compat-util.h"
+#include "config.h"
+#include "fsmonitor-ll.h"
+#include "fsm-health.h"
+#include "fsmonitor--daemon.h"
+
+/*
+ * The Linux fsmonitor implementation uses inotify which has its own
+ * mechanisms for detecting filesystem unmount and other events that
+ * would require the daemon to shutdown.  Therefore, we don't need
+ * a separate health thread like Windows does.
+ *
+ * These stub functions satisfy the interface requirements.
+ */
+
+int fsm_health__ctor(struct fsmonitor_daemon_state *state UNUSED)
+{
+	return 0;
+}
+
+void fsm_health__dtor(struct fsmonitor_daemon_state *state UNUSED)
+{
+	return;
+}
+
+void fsm_health__loop(struct fsmonitor_daemon_state *state UNUSED)
+{
+	return;
+}
+
+void fsm_health__stop_async(struct fsmonitor_daemon_state *state UNUSED)
+{
+}
diff --git a/compat/fsmonitor/fsm-ipc-darwin.c b/compat/fsmonitor/fsm-ipc-unix.c
similarity index 100%
rename from compat/fsmonitor/fsm-ipc-darwin.c
rename to compat/fsmonitor/fsm-ipc-unix.c
diff --git a/compat/fsmonitor/fsm-listen-linux.c b/compat/fsmonitor/fsm-listen-linux.c
new file mode 100644
index 0000000..e3dca14
--- /dev/null
+++ b/compat/fsmonitor/fsm-listen-linux.c
@@ -0,0 +1,746 @@
+#include "git-compat-util.h"
+#include "dir.h"
+#include "fsmonitor-ll.h"
+#include "fsm-listen.h"
+#include "fsmonitor--daemon.h"
+#include "fsmonitor-path-utils.h"
+#include "gettext.h"
+#include "simple-ipc.h"
+#include "string-list.h"
+#include "trace.h"
+
+#include <sys/inotify.h>
+
+/*
+ * Safe value to bitwise OR with rest of mask for
+ * kernels that do not support IN_MASK_CREATE
+ */
+#ifndef IN_MASK_CREATE
+#define IN_MASK_CREATE 0x00000000
+#endif
+
+enum shutdown_reason {
+	SHUTDOWN_CONTINUE = 0,
+	SHUTDOWN_STOP,
+	SHUTDOWN_ERROR,
+	SHUTDOWN_FORCE
+};
+
+struct watch_entry {
+	struct hashmap_entry ent;
+	int wd;
+	uint32_t cookie;
+	const char *dir;
+};
+
+struct rename_entry {
+	struct hashmap_entry ent;
+	time_t whence;
+	uint32_t cookie;
+	const char *dir;
+};
+
+struct fsm_listen_data {
+	int fd_inotify;
+	enum shutdown_reason shutdown;
+	struct hashmap watches;
+	struct hashmap renames;
+	struct hashmap revwatches;
+};
+
+static int watch_entry_cmp(const void *cmp_data UNUSED,
+			   const struct hashmap_entry *eptr,
+			   const struct hashmap_entry *entry_or_key,
+			   const void *keydata UNUSED)
+{
+	const struct watch_entry *e1, *e2;
+
+	e1 = container_of(eptr, const struct watch_entry, ent);
+	e2 = container_of(entry_or_key, const struct watch_entry, ent);
+	return e1->wd != e2->wd;
+}
+
+static int revwatches_entry_cmp(const void *cmp_data UNUSED,
+				const struct hashmap_entry *eptr,
+				const struct hashmap_entry *entry_or_key,
+				const void *keydata UNUSED)
+{
+	const struct watch_entry *e1, *e2;
+
+	e1 = container_of(eptr, const struct watch_entry, ent);
+	e2 = container_of(entry_or_key, const struct watch_entry, ent);
+	return strcmp(e1->dir, e2->dir);
+}
+
+static int rename_entry_cmp(const void *cmp_data UNUSED,
+			    const struct hashmap_entry *eptr,
+			    const struct hashmap_entry *entry_or_key,
+			    const void *keydata UNUSED)
+{
+	const struct rename_entry *e1, *e2;
+
+	e1 = container_of(eptr, const struct rename_entry, ent);
+	e2 = container_of(entry_or_key, const struct rename_entry, ent);
+	return e1->cookie != e2->cookie;
+}
+
+/*
+ * Register an inotify watch, add watch descriptor to path mapping
+ * and the reverse mapping.
+ */
+static int add_watch(const char *path, struct fsm_listen_data *data)
+{
+	const char *interned = strintern(path);
+	struct watch_entry *w1, *w2;
+
+	/* add the inotify watch, don't allow watches to be modified */
+	int wd = inotify_add_watch(data->fd_inotify, interned,
+				   (IN_ALL_EVENTS | IN_ONLYDIR | IN_MASK_CREATE)
+				    ^ IN_ACCESS ^ IN_CLOSE ^ IN_OPEN);
+	if (wd < 0) {
+		if (errno == ENOENT || errno == ENOTDIR)
+			return 0; /* directory was deleted or is not a directory */
+		if (errno == EEXIST)
+			return 0; /* watch already exists, no action needed */
+		if (errno == ENOSPC)
+			return error(_("inotify watch limit reached; "
+				       "increase fs.inotify.max_user_watches"));
+		return error_errno(_("inotify_add_watch('%s') failed"), interned);
+	}
+
+	/* add watch descriptor -> directory mapping */
+	CALLOC_ARRAY(w1, 1);
+	w1->wd = wd;
+	w1->dir = interned;
+	hashmap_entry_init(&w1->ent, memhash(&w1->wd, sizeof(int)));
+	hashmap_add(&data->watches, &w1->ent);
+
+	/* add directory -> watch descriptor mapping */
+	CALLOC_ARRAY(w2, 1);
+	w2->wd = wd;
+	w2->dir = interned;
+	hashmap_entry_init(&w2->ent, strhash(w2->dir));
+	hashmap_add(&data->revwatches, &w2->ent);
+
+	return 0;
+}
+
+/*
+ * Remove the inotify watch, the watch descriptor to path mapping
+ * and the reverse mapping.
+ */
+static void remove_watch(struct watch_entry *w, struct fsm_listen_data *data)
+{
+	struct watch_entry k1, k2, *w1, *w2;
+
+	/* remove watch, ignore error if kernel already did it */
+	if (inotify_rm_watch(data->fd_inotify, w->wd) && errno != EINVAL)
+		error_errno(_("inotify_rm_watch() failed"));
+
+	k1.wd = w->wd;
+	hashmap_entry_init(&k1.ent, memhash(&k1.wd, sizeof(int)));
+	w1 = hashmap_remove_entry(&data->watches, &k1, ent, NULL);
+	if (!w1)
+		BUG("double remove of watch for '%s'", w->dir);
+
+	if (w1->cookie)
+		BUG("removing watch for '%s' which has a pending rename", w1->dir);
+
+	k2.dir = w->dir;
+	hashmap_entry_init(&k2.ent, strhash(k2.dir));
+	w2 = hashmap_remove_entry(&data->revwatches, &k2, ent, NULL);
+	if (!w2)
+		BUG("double remove of reverse watch for '%s'", w->dir);
+
+	/* w1->dir and w2->dir are interned strings, we don't own them */
+	free(w1);
+	free(w2);
+}
+
+/*
+ * Check for stale directory renames.
+ *
+ * https://man7.org/linux/man-pages/man7/inotify.7.html
+ *
+ * Allow for some small timeout to account for the fact that insertion of the
+ * IN_MOVED_FROM+IN_MOVED_TO event pair is not atomic, and the possibility that
+ * there may not be any IN_MOVED_TO event.
+ *
+ * If the IN_MOVED_TO event is not received within the timeout then events have
+ * been missed and the monitor is in an inconsistent state with respect to the
+ * filesystem.
+ */
+static int check_stale_dir_renames(struct hashmap *renames, time_t max_age)
+{
+	struct rename_entry *re;
+	struct hashmap_iter iter;
+
+	hashmap_for_each_entry(renames, &iter, re, ent) {
+		if (re->whence <= max_age)
+			return -1;
+	}
+	return 0;
+}
+
+/*
+ * Track pending renames.
+ *
+ * Tracking is done via an event cookie to watch descriptor mapping.
+ *
+ * A rename is not complete until matching an IN_MOVED_TO event is received
+ * for a corresponding IN_MOVED_FROM event.
+ */
+static void add_dir_rename(uint32_t cookie, const char *path,
+			   struct fsm_listen_data *data)
+{
+	struct watch_entry k, *w;
+	struct rename_entry *re;
+
+	/* lookup the watch descriptor for the given path */
+	k.dir = path;
+	hashmap_entry_init(&k.ent, strhash(path));
+	w = hashmap_get_entry(&data->revwatches, &k, ent, NULL);
+	if (!w) {
+		/*
+		 * This can happen in rare cases where the directory was
+		 * moved before we had a chance to add a watch on it.
+		 * Just ignore this rename.
+		 */
+		trace_printf_key(&trace_fsmonitor,
+				 "no watch found for rename from '%s'", path);
+		return;
+	}
+	w->cookie = cookie;
+
+	/* add the pending rename to match against later */
+	CALLOC_ARRAY(re, 1);
+	re->dir = w->dir;
+	re->cookie = w->cookie;
+	re->whence = time(NULL);
+	hashmap_entry_init(&re->ent, memhash(&re->cookie, sizeof(uint32_t)));
+	hashmap_add(&data->renames, &re->ent);
+}
+
+/*
+ * Handle directory renames
+ *
+ * Once an IN_MOVED_TO event is received, lookup the rename tracking information
+ * via the event cookie and use this information to update the watch.
+ */
+static void rename_dir(uint32_t cookie, const char *path,
+		       struct fsm_listen_data *data)
+{
+	struct rename_entry rek, *re;
+	struct watch_entry k, *w;
+
+	/* lookup a pending rename to match */
+	rek.cookie = cookie;
+	hashmap_entry_init(&rek.ent, memhash(&rek.cookie, sizeof(uint32_t)));
+	re = hashmap_get_entry(&data->renames, &rek, ent, NULL);
+	if (re) {
+		k.dir = re->dir;
+		hashmap_entry_init(&k.ent, strhash(k.dir));
+		w = hashmap_get_entry(&data->revwatches, &k, ent, NULL);
+		if (w) {
+			w->cookie = 0; /* rename handled */
+			remove_watch(w, data);
+			if (add_watch(path, data))
+				trace_printf_key(&trace_fsmonitor,
+						 "failed to add watch for renamed dir '%s'",
+						 path);
+		} else {
+			/* Directory was moved out of watch tree */
+			trace_printf_key(&trace_fsmonitor,
+					 "no matching watch for rename to '%s'", path);
+		}
+		hashmap_remove_entry(&data->renames, &rek, ent, NULL);
+		free(re);
+	} else {
+		/* Directory was moved from outside the watch tree */
+		trace_printf_key(&trace_fsmonitor,
+				 "no matching cookie for rename to '%s'", path);
+	}
+}
+
+/*
+ * Recursively add watches to every directory under path
+ */
+static int register_inotify(const char *path,
+			    struct fsmonitor_daemon_state *state,
+			    struct fsmonitor_batch *batch)
+{
+	DIR *dir;
+	const char *rel;
+	struct strbuf current = STRBUF_INIT;
+	struct dirent *de;
+	struct stat fs;
+	int ret = -1;
+
+	dir = opendir(path);
+	if (!dir) {
+		if (errno == ENOENT || errno == ENOTDIR)
+			return 0; /* directory was deleted */
+		return error_errno(_("opendir('%s') failed"), path);
+	}
+
+	while ((de = readdir_skip_dot_and_dotdot(dir)) != NULL) {
+		strbuf_reset(&current);
+		strbuf_addf(&current, "%s/%s", path, de->d_name);
+		if (lstat(current.buf, &fs)) {
+			if (errno == ENOENT)
+				continue; /* file was deleted */
+			error_errno(_("lstat('%s') failed"), current.buf);
+			goto failed;
+		}
+
+		/* recurse into directory */
+		if (S_ISDIR(fs.st_mode)) {
+			if (add_watch(current.buf, state->listen_data))
+				goto failed;
+			if (register_inotify(current.buf, state, batch))
+				goto failed;
+		} else if (batch) {
+			rel = current.buf + state->path_worktree_watch.len + 1;
+			trace_printf_key(&trace_fsmonitor, "explicitly adding '%s'", rel);
+			fsmonitor_batch__add_path(batch, rel);
+		}
+	}
+	ret = 0;
+
+failed:
+	strbuf_release(&current);
+	if (closedir(dir) < 0)
+		return error_errno(_("closedir('%s') failed"), path);
+	return ret;
+}
+
+static int em_rename_dir_from(uint32_t mask)
+{
+	return ((mask & IN_ISDIR) && (mask & IN_MOVED_FROM));
+}
+
+static int em_rename_dir_to(uint32_t mask)
+{
+	return ((mask & IN_ISDIR) && (mask & IN_MOVED_TO));
+}
+
+static int em_remove_watch(uint32_t mask)
+{
+	return (mask & IN_DELETE_SELF);
+}
+
+static int em_dir_renamed(uint32_t mask)
+{
+	return ((mask & IN_ISDIR) && (mask & IN_MOVE));
+}
+
+static int em_dir_created(uint32_t mask)
+{
+	return ((mask & IN_ISDIR) && (mask & IN_CREATE));
+}
+
+static int em_dir_deleted(uint32_t mask)
+{
+	return ((mask & IN_ISDIR) && (mask & IN_DELETE));
+}
+
+static int em_force_shutdown(uint32_t mask)
+{
+	return (mask & IN_UNMOUNT) || (mask & IN_Q_OVERFLOW);
+}
+
+static int em_ignore(uint32_t mask)
+{
+	return (mask & IN_IGNORED) || (mask & IN_MOVE_SELF);
+}
+
+static void log_mask_set(const char *path, uint32_t mask)
+{
+	struct strbuf msg = STRBUF_INIT;
+
+	if (mask & IN_ACCESS)
+		strbuf_addstr(&msg, "IN_ACCESS|");
+	if (mask & IN_MODIFY)
+		strbuf_addstr(&msg, "IN_MODIFY|");
+	if (mask & IN_ATTRIB)
+		strbuf_addstr(&msg, "IN_ATTRIB|");
+	if (mask & IN_CLOSE_WRITE)
+		strbuf_addstr(&msg, "IN_CLOSE_WRITE|");
+	if (mask & IN_CLOSE_NOWRITE)
+		strbuf_addstr(&msg, "IN_CLOSE_NOWRITE|");
+	if (mask & IN_OPEN)
+		strbuf_addstr(&msg, "IN_OPEN|");
+	if (mask & IN_MOVED_FROM)
+		strbuf_addstr(&msg, "IN_MOVED_FROM|");
+	if (mask & IN_MOVED_TO)
+		strbuf_addstr(&msg, "IN_MOVED_TO|");
+	if (mask & IN_CREATE)
+		strbuf_addstr(&msg, "IN_CREATE|");
+	if (mask & IN_DELETE)
+		strbuf_addstr(&msg, "IN_DELETE|");
+	if (mask & IN_DELETE_SELF)
+		strbuf_addstr(&msg, "IN_DELETE_SELF|");
+	if (mask & IN_MOVE_SELF)
+		strbuf_addstr(&msg, "IN_MOVE_SELF|");
+	if (mask & IN_UNMOUNT)
+		strbuf_addstr(&msg, "IN_UNMOUNT|");
+	if (mask & IN_Q_OVERFLOW)
+		strbuf_addstr(&msg, "IN_Q_OVERFLOW|");
+	if (mask & IN_IGNORED)
+		strbuf_addstr(&msg, "IN_IGNORED|");
+	if (mask & IN_ISDIR)
+		strbuf_addstr(&msg, "IN_ISDIR|");
+
+	strbuf_strip_suffix(&msg, "|");
+
+	trace_printf_key(&trace_fsmonitor, "inotify_event: '%s', mask=%#8.8x %s",
+			 path, mask, msg.buf);
+
+	strbuf_release(&msg);
+}
+
+int fsm_listen__ctor(struct fsmonitor_daemon_state *state)
+{
+	int fd;
+	int ret = 0;
+	struct fsm_listen_data *data;
+
+	CALLOC_ARRAY(data, 1);
+	state->listen_data = data;
+	state->listen_error_code = -1;
+	data->fd_inotify = -1;
+	data->shutdown = SHUTDOWN_ERROR;
+
+	fd = inotify_init1(O_NONBLOCK);
+	if (fd < 0) {
+		FREE_AND_NULL(state->listen_data);
+		return error_errno(_("inotify_init1() failed"));
+	}
+
+	data->fd_inotify = fd;
+
+	hashmap_init(&data->watches, watch_entry_cmp, NULL, 0);
+	hashmap_init(&data->renames, rename_entry_cmp, NULL, 0);
+	hashmap_init(&data->revwatches, revwatches_entry_cmp, NULL, 0);
+
+	if (add_watch(state->path_worktree_watch.buf, data))
+		ret = -1;
+	else if (register_inotify(state->path_worktree_watch.buf, state, NULL))
+		ret = -1;
+	else if (state->nr_paths_watching > 1) {
+		if (add_watch(state->path_gitdir_watch.buf, data))
+			ret = -1;
+		else if (register_inotify(state->path_gitdir_watch.buf, state, NULL))
+			ret = -1;
+	}
+
+	if (!ret) {
+		state->listen_error_code = 0;
+		data->shutdown = SHUTDOWN_CONTINUE;
+	}
+
+	return ret;
+}
+
+void fsm_listen__dtor(struct fsmonitor_daemon_state *state)
+{
+	struct fsm_listen_data *data;
+	struct hashmap_iter iter;
+	struct watch_entry *w;
+	struct watch_entry **to_remove;
+	size_t nr_to_remove = 0, alloc_to_remove = 0;
+	size_t i;
+	int fd;
+
+	if (!state || !state->listen_data)
+		return;
+
+	data = state->listen_data;
+	fd = data->fd_inotify;
+
+	/*
+	 * Collect all entries first, then remove them.
+	 * We can't modify the hashmap while iterating over it.
+	 */
+	to_remove = NULL;
+	hashmap_for_each_entry(&data->watches, &iter, w, ent) {
+		ALLOC_GROW(to_remove, nr_to_remove + 1, alloc_to_remove);
+		to_remove[nr_to_remove++] = w;
+	}
+
+	for (i = 0; i < nr_to_remove; i++) {
+		to_remove[i]->cookie = 0; /* ignore any pending renames */
+		remove_watch(to_remove[i], data);
+	}
+	free(to_remove);
+
+	hashmap_clear(&data->watches);
+
+	hashmap_clear(&data->revwatches); /* remove_watch freed the entries */
+
+	hashmap_clear_and_free(&data->renames, struct rename_entry, ent);
+
+	FREE_AND_NULL(state->listen_data);
+
+	if (fd >= 0 && (close(fd) < 0))
+		error_errno(_("closing inotify file descriptor failed"));
+}
+
+void fsm_listen__stop_async(struct fsmonitor_daemon_state *state)
+{
+	if (state && state->listen_data &&
+	    state->listen_data->shutdown == SHUTDOWN_CONTINUE)
+		state->listen_data->shutdown = SHUTDOWN_STOP;
+}
+
+/*
+ * Process a single inotify event and queue for publication.
+ */
+static int process_event(const char *path,
+			 const struct inotify_event *event,
+			 struct fsmonitor_batch **batch,
+			 struct string_list *cookie_list,
+			 struct fsmonitor_daemon_state *state)
+{
+	const char *rel;
+	const char *last_sep;
+
+	switch (fsmonitor_classify_path_absolute(state, path)) {
+	case IS_INSIDE_DOT_GIT_WITH_COOKIE_PREFIX:
+	case IS_INSIDE_GITDIR_WITH_COOKIE_PREFIX:
+		/* Use just the filename of the cookie file. */
+		last_sep = find_last_dir_sep(path);
+		string_list_append(cookie_list,
+				   last_sep ? last_sep + 1 : path);
+		break;
+	case IS_INSIDE_DOT_GIT:
+	case IS_INSIDE_GITDIR:
+		break;
+	case IS_DOT_GIT:
+	case IS_GITDIR:
+		/*
+		 * If .git directory is deleted or renamed away,
+		 * we have to quit.
+		 */
+		if (em_dir_deleted(event->mask)) {
+			trace_printf_key(&trace_fsmonitor,
+					 "event: gitdir removed");
+			state->listen_data->shutdown = SHUTDOWN_FORCE;
+			goto done;
+		}
+
+		if (em_dir_renamed(event->mask)) {
+			trace_printf_key(&trace_fsmonitor,
+					 "event: gitdir renamed");
+			state->listen_data->shutdown = SHUTDOWN_FORCE;
+			goto done;
+		}
+		break;
+	case IS_WORKDIR_PATH:
+		/* normal events in the working directory */
+		if (trace_pass_fl(&trace_fsmonitor))
+			log_mask_set(path, event->mask);
+
+		if (!*batch)
+			*batch = fsmonitor_batch__new();
+
+		rel = path + state->path_worktree_watch.len + 1;
+		fsmonitor_batch__add_path(*batch, rel);
+
+		if (em_dir_deleted(event->mask))
+			break;
+
+		/* received IN_MOVE_FROM, add tracking for expected IN_MOVE_TO */
+		if (em_rename_dir_from(event->mask))
+			add_dir_rename(event->cookie, path, state->listen_data);
+
+		/* received IN_MOVE_TO, update watch to reflect new path */
+		if (em_rename_dir_to(event->mask)) {
+			rename_dir(event->cookie, path, state->listen_data);
+			if (register_inotify(path, state, *batch)) {
+				state->listen_data->shutdown = SHUTDOWN_ERROR;
+				goto done;
+			}
+		}
+
+		if (em_dir_created(event->mask)) {
+			if (add_watch(path, state->listen_data)) {
+				state->listen_data->shutdown = SHUTDOWN_ERROR;
+				goto done;
+			}
+			if (register_inotify(path, state, *batch)) {
+				state->listen_data->shutdown = SHUTDOWN_ERROR;
+				goto done;
+			}
+		}
+		break;
+	case IS_OUTSIDE_CONE:
+	default:
+		trace_printf_key(&trace_fsmonitor,
+				 "ignoring '%s'", path);
+		break;
+	}
+	return 0;
+done:
+	return -1;
+}
+
+/*
+ * Read the inotify event stream and pre-process events before further
+ * processing and eventual publishing.
+ */
+static void handle_events(struct fsmonitor_daemon_state *state)
+{
+	/* See https://man7.org/linux/man-pages/man7/inotify.7.html */
+	char buf[4096]
+		__attribute__ ((aligned(__alignof__(struct inotify_event))));
+
+	struct hashmap *watches = &state->listen_data->watches;
+	struct fsmonitor_batch *batch = NULL;
+	struct string_list cookie_list = STRING_LIST_INIT_DUP;
+	struct watch_entry k, *w;
+	struct strbuf path = STRBUF_INIT;
+	const struct inotify_event *event;
+	int fd = state->listen_data->fd_inotify;
+	ssize_t len;
+	char *ptr, *p;
+
+	for (;;) {
+		len = read(fd, buf, sizeof(buf));
+		if (len == -1) {
+			if (errno == EAGAIN || errno == EINTR)
+				goto done;
+			error_errno(_("reading inotify message stream failed"));
+			state->listen_data->shutdown = SHUTDOWN_ERROR;
+			goto done;
+		}
+
+		/* nothing to read */
+		if (len == 0)
+			goto done;
+
+		/* Loop over all events in the buffer. */
+		for (ptr = buf; ptr < buf + len;
+		     ptr += sizeof(struct inotify_event) + event->len) {
+
+			event = (const struct inotify_event *)ptr;
+
+			if (em_ignore(event->mask))
+				continue;
+
+			/* File system was unmounted or event queue overflowed */
+			if (em_force_shutdown(event->mask)) {
+				if (trace_pass_fl(&trace_fsmonitor))
+					log_mask_set("forcing shutdown", event->mask);
+				state->listen_data->shutdown = SHUTDOWN_FORCE;
+				goto done;
+			}
+
+			k.wd = event->wd;
+			hashmap_entry_init(&k.ent, memhash(&k.wd, sizeof(int)));
+
+			w = hashmap_get_entry(watches, &k, ent, NULL);
+			if (!w) {
+				/* Watch was removed, skip event */
+				continue;
+			}
+
+			/* directory watch was removed */
+			if (em_remove_watch(event->mask)) {
+				remove_watch(w, state->listen_data);
+				continue;
+			}
+
+			strbuf_reset(&path);
+			strbuf_addf(&path, "%s/%s", w->dir, event->name);
+
+			p = fsmonitor__resolve_alias(path.buf, &state->alias);
+			if (!p)
+				p = strbuf_detach(&path, NULL);
+
+			if (process_event(p, event, &batch, &cookie_list, state)) {
+				free(p);
+				goto done;
+			}
+			free(p);
+		}
+		strbuf_reset(&path);
+		fsmonitor_publish(state, batch, &cookie_list);
+		string_list_clear(&cookie_list, 0);
+		batch = NULL;
+	}
+done:
+	strbuf_release(&path);
+	fsmonitor_batch__free_list(batch);
+	string_list_clear(&cookie_list, 0);
+}
+
+/*
+ * Non-blocking read of the inotify events stream. The inotify fd is polled
+ * frequently to help minimize the number of queue overflows.
+ */
+void fsm_listen__loop(struct fsmonitor_daemon_state *state)
+{
+	int poll_num;
+	/*
+	 * Interval in seconds between checks for stale directory renames.
+	 * A directory rename that is not completed within this window
+	 * (i.e. no matching IN_MOVED_TO for an IN_MOVED_FROM) indicates
+	 * missed events, forcing a shutdown.
+	 */
+	const int interval = 1;
+	time_t checked = time(NULL);
+	struct pollfd fds[1];
+
+	fds[0].fd = state->listen_data->fd_inotify;
+	fds[0].events = POLLIN;
+
+	/*
+	 * Our fs event listener is now running, so it's safe to start
+	 * serving client requests.
+	 */
+	ipc_server_start_async(state->ipc_server_data);
+
+	for (;;) {
+		switch (state->listen_data->shutdown) {
+		case SHUTDOWN_CONTINUE:
+			poll_num = poll(fds, 1, 50);
+			if (poll_num == -1) {
+				if (errno == EINTR)
+					continue;
+				error_errno(_("polling inotify message stream failed"));
+				state->listen_data->shutdown = SHUTDOWN_ERROR;
+				continue;
+			}
+
+			if ((time(NULL) - checked) >= interval) {
+				checked = time(NULL);
+				if (check_stale_dir_renames(&state->listen_data->renames,
+							    checked - interval)) {
+					trace_printf_key(&trace_fsmonitor,
+							 "missed IN_MOVED_TO events, forcing shutdown");
+					state->listen_data->shutdown = SHUTDOWN_FORCE;
+					continue;
+				}
+			}
+
+			if (poll_num > 0 && (fds[0].revents & POLLIN))
+				handle_events(state);
+
+			continue;
+		case SHUTDOWN_ERROR:
+			state->listen_error_code = -1;
+			ipc_server_stop_async(state->ipc_server_data);
+			break;
+		case SHUTDOWN_FORCE:
+			state->listen_error_code = 0;
+			ipc_server_stop_async(state->ipc_server_data);
+			break;
+		case SHUTDOWN_STOP:
+		default:
+			state->listen_error_code = 0;
+			break;
+		}
+		return;
+	}
+}
diff --git a/compat/fsmonitor/fsm-path-utils-linux.c b/compat/fsmonitor/fsm-path-utils-linux.c
new file mode 100644
index 0000000..c9866b1
--- /dev/null
+++ b/compat/fsmonitor/fsm-path-utils-linux.c
@@ -0,0 +1,217 @@
+#include "git-compat-util.h"
+#include "fsmonitor-ll.h"
+#include "fsmonitor-path-utils.h"
+#include "gettext.h"
+#include "trace.h"
+
+#include <sys/statfs.h>
+
+#ifdef HAVE_LINUX_MAGIC_H
+#include <linux/magic.h>
+#endif
+
+/*
+ * Filesystem magic numbers for remote filesystems.
+ * Defined here if not available in linux/magic.h.
+ */
+#ifndef CIFS_SUPER_MAGIC
+#define CIFS_SUPER_MAGIC 0xff534d42
+#endif
+#ifndef SMB_SUPER_MAGIC
+#define SMB_SUPER_MAGIC 0x517b
+#endif
+#ifndef SMB2_SUPER_MAGIC
+#define SMB2_SUPER_MAGIC 0xfe534d42
+#endif
+#ifndef NFS_SUPER_MAGIC
+#define NFS_SUPER_MAGIC 0x6969
+#endif
+#ifndef AFS_SUPER_MAGIC
+#define AFS_SUPER_MAGIC 0x5346414f
+#endif
+#ifndef CODA_SUPER_MAGIC
+#define CODA_SUPER_MAGIC 0x73757245
+#endif
+#ifndef FUSE_SUPER_MAGIC
+#define FUSE_SUPER_MAGIC 0x65735546
+#endif
+
+/*
+ * Check if filesystem type is a remote filesystem.
+ */
+static int is_remote_fs(unsigned long f_type)
+{
+	switch (f_type) {
+	case CIFS_SUPER_MAGIC:
+	case SMB_SUPER_MAGIC:
+	case SMB2_SUPER_MAGIC:
+	case NFS_SUPER_MAGIC:
+	case AFS_SUPER_MAGIC:
+	case CODA_SUPER_MAGIC:
+	case FUSE_SUPER_MAGIC:
+		return 1;
+	default:
+		return 0;
+	}
+}
+
+/*
+ * Map filesystem magic numbers to human-readable names as a fallback
+ * when /proc/mounts is unavailable.  This only covers the remote and
+ * special filesystems in is_remote_fs() above; local filesystems are
+ * never flagged as incompatible, so we do not need their names here.
+ */
+static const char *get_fs_typename(unsigned long f_type)
+{
+	switch (f_type) {
+	case CIFS_SUPER_MAGIC:
+		return "cifs";
+	case SMB_SUPER_MAGIC:
+		return "smb";
+	case SMB2_SUPER_MAGIC:
+		return "smb2";
+	case NFS_SUPER_MAGIC:
+		return "nfs";
+	case AFS_SUPER_MAGIC:
+		return "afs";
+	case CODA_SUPER_MAGIC:
+		return "coda";
+	case FUSE_SUPER_MAGIC:
+		return "fuse";
+	default:
+		return "unknown";
+	}
+}
+
+/*
+ * Find the mount point for a given path by reading /proc/mounts.
+ *
+ * statfs(2) gives us f_type (the magic number) but not the human-readable
+ * filesystem type string.  We scan /proc/mounts to find the mount entry
+ * whose path is the longest prefix of ours and whose f_fsid matches,
+ * which gives us the fstype string (e.g. "nfs", "ext4") for logging.
+ */
+static char *find_mount(const char *path, const struct statfs *path_fs)
+{
+	FILE *fp;
+	struct strbuf line = STRBUF_INIT;
+	struct strbuf match = STRBUF_INIT;
+	struct strbuf fstype = STRBUF_INIT;
+	char *result = NULL;
+
+	fp = fopen("/proc/mounts", "r");
+	if (!fp)
+		return NULL;
+
+	while (strbuf_getline(&line, fp) != EOF) {
+		char *fields[6];
+		char *p = line.buf;
+		int i;
+
+		/* Parse mount entry: device mountpoint fstype options dump pass */
+		for (i = 0; i < 6 && p; i++) {
+			fields[i] = p;
+			p = strchr(p, ' ');
+			if (p)
+				*p++ = '\0';
+		}
+
+		if (i >= 3) {
+			const char *mountpoint = fields[1];
+			const char *type = fields[2];
+			struct statfs mount_fs;
+
+			/* Check if this mount point is a prefix of our path */
+			if (starts_with(path, mountpoint) &&
+			    (path[strlen(mountpoint)] == '/' ||
+			     path[strlen(mountpoint)] == '\0')) {
+				/* Check if filesystem ID matches */
+				if (statfs(mountpoint, &mount_fs) == 0 &&
+				    !memcmp(&mount_fs.f_fsid, &path_fs->f_fsid,
+					    sizeof(mount_fs.f_fsid))) {
+					/* Keep the longest matching mount point */
+					if (strlen(mountpoint) > match.len) {
+						strbuf_reset(&match);
+						strbuf_addstr(&match, mountpoint);
+						strbuf_reset(&fstype);
+						strbuf_addstr(&fstype, type);
+					}
+				}
+			}
+		}
+	}
+
+	fclose(fp);
+	strbuf_release(&line);
+	strbuf_release(&match);
+
+	if (fstype.len)
+		result = strbuf_detach(&fstype, NULL);
+	else
+		strbuf_release(&fstype);
+
+	return result;
+}
+
+int fsmonitor__get_fs_info(const char *path, struct fs_info *fs_info)
+{
+	struct statfs fs;
+
+	if (statfs(path, &fs) == -1) {
+		int saved_errno = errno;
+		trace_printf_key(&trace_fsmonitor, "statfs('%s') failed: %s",
+				 path, strerror(saved_errno));
+		errno = saved_errno;
+		return -1;
+	}
+
+	trace_printf_key(&trace_fsmonitor,
+			 "statfs('%s') [type 0x%08lx]",
+			 path, (unsigned long)fs.f_type);
+
+	fs_info->is_remote = is_remote_fs(fs.f_type);
+
+	/*
+	 * Try to get filesystem type from /proc/mounts for a more
+	 * descriptive name.
+	 */
+	fs_info->typename = find_mount(path, &fs);
+	if (!fs_info->typename)
+		fs_info->typename = xstrdup(get_fs_typename(fs.f_type));
+
+	trace_printf_key(&trace_fsmonitor,
+			 "'%s' is_remote: %d, typename: %s",
+			 path, fs_info->is_remote, fs_info->typename);
+
+	return 0;
+}
+
+int fsmonitor__is_fs_remote(const char *path)
+{
+	struct fs_info fs;
+
+	if (fsmonitor__get_fs_info(path, &fs))
+		return -1;
+
+	free(fs.typename);
+
+	return fs.is_remote;
+}
+
+/*
+ * No-op for Linux - we don't have firmlinks like macOS.
+ */
+int fsmonitor__get_alias(const char *path UNUSED,
+			 struct alias_info *info UNUSED)
+{
+	return 0;
+}
+
+/*
+ * No-op for Linux - we don't have firmlinks like macOS.
+ */
+char *fsmonitor__resolve_alias(const char *path UNUSED,
+			       const struct alias_info *info UNUSED)
+{
+	return NULL;
+}
diff --git a/compat/fsmonitor/fsm-settings-darwin.c b/compat/fsmonitor/fsm-settings-unix.c
similarity index 100%
rename from compat/fsmonitor/fsm-settings-darwin.c
rename to compat/fsmonitor/fsm-settings-unix.c
diff --git a/compat/mingw.c b/compat/mingw.c
index 2023c16..aa7525f 100644
--- a/compat/mingw.c
+++ b/compat/mingw.c
@@ -449,20 +449,63 @@ static wchar_t *normalize_ntpath(wchar_t *wbuf)
 	return wbuf;
 }
 
+/*
+ * Use SetFileInformationByHandle(FileDispositionInfo) to force legacy
+ * (non-POSIX) delete semantics. On Windows 11, DeleteFileW() uses POSIX
+ * delete semantics internally, allowing deletion even with active
+ * MapViewOfFile views. This helper simulates Windows 10 behavior where
+ * deletion fails if a file mapping exists.
+ *
+ * Returns nonzero on success (like DeleteFileW), 0 on failure.
+ */
+static int legacy_delete_file(const wchar_t *wpathname)
+{
+	FILE_DISPOSITION_INFO fdi = { TRUE };
+	DWORD gle;
+	HANDLE h = CreateFileW(wpathname, DELETE,
+			       FILE_SHARE_READ | FILE_SHARE_WRITE |
+			       FILE_SHARE_DELETE,
+			       NULL, OPEN_EXISTING,
+			       FILE_FLAG_OPEN_REPARSE_POINT, NULL);
+	if (h == INVALID_HANDLE_VALUE)
+		return 0;
+
+	if (SetFileInformationByHandle(h, FileDispositionInfo,
+				       &fdi, sizeof(fdi))) {
+		CloseHandle(h);
+		return 1;
+	}
+	gle = GetLastError();
+	CloseHandle(h);
+	SetLastError(gle);
+	return 0;
+}
+
+static int try_delete_file(const wchar_t *wpathname, int use_legacy)
+{
+	if (use_legacy)
+		return legacy_delete_file(wpathname);
+	return DeleteFileW(wpathname);
+}
+
 int mingw_unlink(const char *pathname, int handle_in_use_error)
 {
+	static int use_legacy_delete = -1;
 	int tries = 0;
 	wchar_t wpathname[MAX_PATH];
 	if (xutftowcs_path(wpathname, pathname) < 0)
 		return -1;
 
-	if (DeleteFileW(wpathname))
+	if (use_legacy_delete < 0)
+		use_legacy_delete = git_env_bool("GIT_TEST_LEGACY_DELETE", 0);
+
+	if (try_delete_file(wpathname, use_legacy_delete))
 		return 0;
 
 	do {
 		/* read-only files cannot be removed */
 		_wchmod(wpathname, 0666);
-		if (!_wunlink(wpathname))
+		if (try_delete_file(wpathname, use_legacy_delete))
 			return 0;
 		if (!is_file_in_use_error(GetLastError()))
 			break;
diff --git a/compat/nedmalloc/License.txt b/compat/nedmalloc/License.txt
deleted file mode 100644
index 36b7cd9..0000000
--- a/compat/nedmalloc/License.txt
+++ /dev/null
@@ -1,23 +0,0 @@
-Boost Software License - Version 1.0 - August 17th, 2003
-
-Permission is hereby granted, free of charge, to any person or organization
-obtaining a copy of the software and accompanying documentation covered by
-this license (the "Software") to use, reproduce, display, distribute,
-execute, and transmit the Software, and to prepare derivative works of the
-Software, and to permit third-parties to whom the Software is furnished to
-do so, all subject to the following:
-
-The copyright notices in the Software and this entire statement, including
-the above license grant, this restriction and the following disclaimer,
-must be included in all copies of the Software, in whole or in part, and
-all derivative works of the Software, unless such copies or derivative
-works are solely in the form of machine-executable object code generated by
-a source language processor.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
-SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
-FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
-ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
-DEALINGS IN THE SOFTWARE.
diff --git a/compat/nedmalloc/Readme.txt b/compat/nedmalloc/Readme.txt
deleted file mode 100644
index 07cbf50..0000000
--- a/compat/nedmalloc/Readme.txt
+++ /dev/null
@@ -1,136 +0,0 @@
-nedalloc v1.05 15th June 2008:
--=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
-
-by Niall Douglas (http://www.nedprod.com/programs/portable/nedmalloc/)
-
-Enclosed is nedalloc, an alternative malloc implementation for multiple
-threads without lock contention based on dlmalloc v2.8.4. It is more
-or less a newer implementation of ptmalloc2, the standard allocator in
-Linux (which is based on dlmalloc v2.7.0) but also contains a per-thread
-cache for maximum CPU scalability.
-
-It is licensed under the Boost Software License which basically means
-you can do anything you like with it. This does not apply to the malloc.c.h
-file which remains copyright to others.
-
-It has been tested on win32 (x86), win64 (x64), Linux (x64), FreeBSD (x64)
-and Apple MacOS X (x86). It works very well on all of these and is very
-significantly faster than the system allocator on all of these platforms.
-
-By literally dropping in this allocator as a replacement for your system
-allocator, you can see real world improvements of up to three times in normal
-code!
-
-To use:
--=-=-=-
-Drop in nedmalloc.h, nedmalloc.c and malloc.c.h into your project.
-Configure using the instructions in nedmalloc.h. Run and enjoy.
-
-To test, compile test.c. It will run a comparison between your system
-allocator and nedalloc and tell you how much faster nedalloc is. It also
-serves as an example of usage.
-
-Notes:
--=-=-=
-If you want the very latest version of this allocator, get it from the
-TnFOX SVN repository at svn://svn.berlios.de/viewcvs/tnfox/trunk/src/nedmalloc
-
-Because of how nedalloc allocates an mspace per thread, it can cause
-severe bloating of memory usage under certain allocation patterns.
-You can substantially reduce this wastage by setting MAXTHREADSINPOOL
-or the threads parameter to nedcreatepool() to a fraction of the number of
-threads which would normally be in a pool at once. This will reduce
-bloating at the cost of an increase in lock contention. If allocated size
-is less than THREADCACHEMAX, locking is avoided 90-99% of the time and
-if most of your allocations are below this value, you can safely set
-MAXTHREADSINPOOL to one.
-
-You will suffer memory leakage unless you call neddisablethreadcache()
-per pool for every thread which exits. This is because nedalloc cannot
-portably know when a thread exits and thus when its thread cache can
-be returned for use by other code. Don't forget pool zero, the system pool.
-
-For C++ type allocation patterns (where the same sizes of memory are
-regularly allocated and deallocated as objects are created and destroyed),
-the threadcache always benefits performance. If however your allocation
-patterns are different, searching the threadcache may significantly slow
-down your code - as a rule of thumb, if cache utilisation is below 80%
-(see the source for neddisablethreadcache() for how to enable debug
-printing in release mode) then you should disable the thread cache for
-that thread. You can compile out the threadcache code by setting
-THREADCACHEMAX to zero.
-
-Speed comparisons:
--=-=-=-=-=-=-=-=-=
-See Benchmarks.xls for details.
-
-The enclosed test.c can do two things: it can be a torture test or a speed
-test. The speed test is designed to be a representative synthetic
-memory allocator test. It works by randomly mixing allocations with frees
-with half of the allocation sizes being a two power multiple less than
-512 bytes (to mimic C++ stack instantiated objects) and the other half
-being a simple random value less than 16Kb.
-
-The real world code results are from Tn's TestIO benchmark. This is a
-heavily multithreaded and memory intensive benchmark with a lot of branching
-and other stuff modern processors don't like so much. As you'll note, the
-test doesn't show the benefits of the threadcache mostly due to the saturation
-of the memory bus being the limiting factor.
-
-ChangeLog:
--=-=-=-=-=
-v1.05 15th June 2008:
- * { 1042 } Added error check for TLSSET() and TLSFREE() macros. Thanks to
-Markus Elfring for reporting this.
- * { 1043 } Fixed a segfault when freeing memory allocated using
-nedindependent_comalloc(). Thanks to Pavel Vozenilek for reporting this.
-
-v1.04 14th July 2007:
- * Fixed a bug with the new optimised implementation that failed to lock
-on a realloc under certain conditions.
- * Fixed lack of thread synchronisation in InitPool() causing pool corruption
- * Fixed a memory leak of thread cache contents on disabling. Thanks to Earl
-Chew for reporting this.
- * Added a sanity check for freed blocks being valid.
- * Reworked test.c into being a torture test.
- * Fixed GCC assembler optimisation misspecification
-
-v1.04alpha_svn915 7th October 2006:
- * Fixed failure to unlock thread cache list if allocating a new list failed.
-Thanks to Dmitry Chichkov for reporting this. Further thanks to Aleksey Sanin.
- * Fixed realloc(0, <size>) segfaulting. Thanks to Dmitry Chichkov for
-reporting this.
- * Made config defines #ifndef so they can be overridden by the build system.
-Thanks to Aleksey Sanin for suggesting this.
- * Fixed deadlock in nedprealloc() due to unnecessary locking of preferred
-thread mspace when mspace_realloc() always uses the original block's mspace
-anyway. Thanks to Aleksey Sanin for reporting this.
- * Made some speed improvements by hacking mspace_malloc() to no longer lock
-its mspace, thus allowing the recursive mutex implementation to be removed
-with an associated speed increase. Thanks to Aleksey Sanin for suggesting this.
- * Fixed a bug where allocating mspaces overran its max limit. Thanks to
-Aleksey Sanin for reporting this.
-
-v1.03 10th July 2006:
- * Fixed memory corruption bug in threadcache code which only appeared with >4
-threads and in heavy use of the threadcache.
-
-v1.02 15th May 2006:
- * Integrated dlmalloc v2.8.4, fixing the win32 memory release problem and
-improving performance still further. Speed is now up to twice the speed of v1.01
-(average is 67% faster).
- * Fixed win32 critical section implementation. Thanks to Pavel Kuznetsov
-for reporting this.
- * Wasn't locking mspace if all mspaces were locked. Thanks to Pavel Kuznetsov
-for reporting this.
- * Added Apple Mac OS X support.
-
-v1.01 24th February 2006:
- * Fixed multiprocessor scaling problems by removing sources of cache sloshing
- * Earl Chew <earl_chew <at> agilent <dot> com> sent patches for the following:
-   1. size2binidx() wasn't working for default code path (non x86)
-   2. Fixed failure to release mspace lock under certain circumstances which
-      caused a deadlock
-
-v1.00 1st January 2006:
- * First release
diff --git a/compat/nedmalloc/malloc.c.h b/compat/nedmalloc/malloc.c.h
deleted file mode 100644
index e0c5675..0000000
--- a/compat/nedmalloc/malloc.c.h
+++ /dev/null
@@ -1,5761 +0,0 @@
-/*
-  This is a version (aka dlmalloc) of malloc/free/realloc written by
-  Doug Lea and released to the public domain, as explained at
-  http://creativecommons.org/licenses/publicdomain.  Send questions,
-  comments, complaints, performance data, etc to dl@cs.oswego.edu
-
-* Version pre-2.8.4 Mon Nov 27 11:22:37 2006    (dl at gee)
-
-   Note: There may be an updated version of this malloc obtainable at
-	   ftp://gee.cs.oswego.edu/pub/misc/malloc.c
-	 Check before installing!
-
-* Quickstart
-
-  This library is all in one file to simplify the most common usage:
-  ftp it, compile it (-O3), and link it into another program. All of
-  the compile-time options default to reasonable values for use on
-  most platforms.  You might later want to step through various
-  compile-time and dynamic tuning options.
-
-  For convenience, an include file for code using this malloc is at:
-     ftp://gee.cs.oswego.edu/pub/misc/malloc-2.8.4.h
-  You don't really need this .h file unless you call functions not
-  defined in your system include files.  The .h file contains only the
-  excerpts from this file needed for using this malloc on ANSI C/C++
-  systems, so long as you haven't changed compile-time options about
-  naming and tuning parameters.  If you do, then you can create your
-  own malloc.h that does include all settings by cutting at the point
-  indicated below. Note that you may already by default be using a C
-  library containing a malloc that is based on some version of this
-  malloc (for example in linux). You might still want to use the one
-  in this file to customize settings or to avoid overheads associated
-  with library versions.
-
-* Vital statistics:
-
-  Supported pointer/size_t representation:       4 or 8 bytes
-       size_t MUST be an unsigned type of the same width as
-       pointers. (If you are using an ancient system that declares
-       size_t as a signed type, or need it to be a different width
-       than pointers, you can use a previous release of this malloc
-       (e.g. 2.7.2) supporting these.)
-
-  Alignment:                                     8 bytes (default)
-       This suffices for nearly all current machines and C compilers.
-       However, you can define MALLOC_ALIGNMENT to be wider than this
-       if necessary (up to 128bytes), at the expense of using more space.
-
-  Minimum overhead per allocated chunk:   4 or  8 bytes (if 4byte sizes)
-					  8 or 16 bytes (if 8byte sizes)
-       Each malloced chunk has a hidden word of overhead holding size
-       and status information, and additional cross-check word
-       if FOOTERS is defined.
-
-  Minimum allocated size: 4-byte ptrs:  16 bytes    (including overhead)
-			  8-byte ptrs:  32 bytes    (including overhead)
-
-       Even a request for zero bytes (i.e., malloc(0)) returns a
-       pointer to something of the minimum allocatable size.
-       The maximum overhead wastage (i.e., number of extra bytes
-       allocated than were requested in malloc) is less than or equal
-       to the minimum size, except for requests >= mmap_threshold that
-       are serviced via mmap(), where the worst case wastage is about
-       32 bytes plus the remainder from a system page (the minimal
-       mmap unit); typically 4096 or 8192 bytes.
-
-  Security: static-safe; optionally more or less
-       The "security" of malloc refers to the ability of malicious
-       code to accentuate the effects of errors (for example, freeing
-       space that is not currently malloc'ed or overwriting past the
-       ends of chunks) in code that calls malloc.  This malloc
-       guarantees not to modify any memory locations below the base of
-       heap, i.e., static variables, even in the presence of usage
-       errors.  The routines additionally detect most improper frees
-       and reallocs.  All this holds as long as the static bookkeeping
-       for malloc itself is not corrupted by some other means.  This
-       is only one aspect of security -- these checks do not, and
-       cannot, detect all possible programming errors.
-
-       If FOOTERS is defined nonzero, then each allocated chunk
-       carries an additional check word to verify that it was malloced
-       from its space.  These check words are the same within each
-       execution of a program using malloc, but differ across
-       executions, so externally crafted fake chunks cannot be
-       freed. This improves security by rejecting frees/reallocs that
-       could corrupt heap memory, in addition to the checks preventing
-       writes to statics that are always on.  This may further improve
-       security at the expense of time and space overhead.  (Note that
-       FOOTERS may also be worth using with MSPACES.)
-
-       By default detected errors cause the program to abort (calling
-       "abort()"). You can override this to instead proceed past
-       errors by defining PROCEED_ON_ERROR.  In this case, a bad free
-       has no effect, and a malloc that encounters a bad address
-       caused by user overwrites will ignore the bad address by
-       dropping pointers and indices to all known memory. This may
-       be appropriate for programs that should continue if at all
-       possible in the face of programming errors, although they may
-       run out of memory because dropped memory is never reclaimed.
-
-       If you don't like either of these options, you can define
-       CORRUPTION_ERROR_ACTION and USAGE_ERROR_ACTION to do anything
-       else. And if you are sure that your program using malloc has
-       no errors or vulnerabilities, you can define INSECURE to 1,
-       which might (or might not) provide a small performance improvement.
-
-  Thread-safety: NOT thread-safe unless USE_LOCKS defined
-       When USE_LOCKS is defined, each public call to malloc, free,
-       etc is surrounded with either a pthread mutex or a win32
-       spinlock (depending on WIN32). This is not especially fast, and
-       can be a major bottleneck.  It is designed only to provide
-       minimal protection in concurrent environments, and to provide a
-       basis for extensions.  If you are using malloc in a concurrent
-       program, consider instead using nedmalloc
-       (http://www.nedprod.com/programs/portable/nedmalloc/) or
-       ptmalloc (See http://www.malloc.de), which are derived
-       from versions of this malloc.
-
-  System requirements: Any combination of MORECORE and/or MMAP/MUNMAP
-       This malloc can use unix sbrk or any emulation (invoked using
-       the CALL_MORECORE macro) and/or mmap/munmap or any emulation
-       (invoked using CALL_MMAP/CALL_MUNMAP) to get and release system
-       memory.  On most unix systems, it tends to work best if both
-       MORECORE and MMAP are enabled.  On Win32, it uses emulations
-       based on VirtualAlloc. It also uses common C library functions
-       like memset.
-
-  Compliance: I believe it is compliant with the Single Unix Specification
-       (See http://www.unix.org). Also SVID/XPG, ANSI C, and probably
-       others as well.
-
-* Overview of algorithms
-
-  This is not the fastest, most space-conserving, most portable, or
-  most tunable malloc ever written. However it is among the fastest
-  while also being among the most space-conserving, portable and
-  tunable.  Consistent balance across these factors results in a good
-  general-purpose allocator for malloc-intensive programs.
-
-  In most ways, this malloc is a best-fit allocator. Generally, it
-  chooses the best-fitting existing chunk for a request, with ties
-  broken in approximately least-recently-used order. (This strategy
-  normally maintains low fragmentation.) However, for requests less
-  than 256bytes, it deviates from best-fit when there is not an
-  exactly fitting available chunk by preferring to use space adjacent
-  to that used for the previous small request, as well as by breaking
-  ties in approximately most-recently-used order. (These enhance
-  locality of series of small allocations.)  And for very large requests
-  (>= 256Kb by default), it relies on system memory mapping
-  facilities, if supported.  (This helps avoid carrying around and
-  possibly fragmenting memory used only for large chunks.)
-
-  All operations (except malloc_stats and mallinfo) have execution
-  times that are bounded by a constant factor of the number of bits in
-  a size_t, not counting any clearing in calloc or copying in realloc,
-  or actions surrounding MORECORE and MMAP that have times
-  proportional to the number of non-contiguous regions returned by
-  system allocation routines, which is often just 1. In real-time
-  applications, you can optionally suppress segment traversals using
-  NO_SEGMENT_TRAVERSAL, which assures bounded execution even when
-  system allocators return non-contiguous spaces, at the typical
-  expense of carrying around more memory and increased fragmentation.
-
-  The implementation is not very modular and seriously overuses
-  macros. Perhaps someday all C compilers will do as good a job
-  inlining modular code as can now be done by brute-force expansion,
-  but now, enough of them seem not to.
-
-  Some compilers issue a lot of warnings about code that is
-  dead/unreachable only on some platforms, and also about intentional
-  uses of negation on unsigned types. All known cases of each can be
-  ignored.
-
-  For a longer but out of date high-level description, see
-     http://gee.cs.oswego.edu/dl/html/malloc.html
-
-* MSPACES
-  If MSPACES is defined, then in addition to malloc, free, etc.,
-  this file also defines mspace_malloc, mspace_free, etc. These
-  are versions of malloc routines that take an "mspace" argument
-  obtained using create_mspace, to control all internal bookkeeping.
-  If ONLY_MSPACES is defined, only these versions are compiled.
-  So if you would like to use this allocator for only some allocations,
-  and your system malloc for others, you can compile with
-  ONLY_MSPACES and then do something like...
-    static mspace mymspace = create_mspace(0,0); // for example
-    #define mymalloc(bytes)  mspace_malloc(mymspace, bytes)
-
-  (Note: If you only need one instance of an mspace, you can instead
-  use "USE_DL_PREFIX" to relabel the global malloc.)
-
-  You can similarly create thread-local allocators by storing
-  mspaces as thread-locals. For example:
-    static __thread mspace tlms = 0;
-    void*  tlmalloc(size_t bytes) {
-      if (tlms == 0) tlms = create_mspace(0, 0);
-      return mspace_malloc(tlms, bytes);
-    }
-    void  tlfree(void* mem) { mspace_free(tlms, mem); }
-
-  Unless FOOTERS is defined, each mspace is completely independent.
-  You cannot allocate from one and free to another (although
-  conformance is only weakly checked, so usage errors are not always
-  caught). If FOOTERS is defined, then each chunk carries around a tag
-  indicating its originating mspace, and frees are directed to their
-  originating spaces.
-
- -------------------------  Compile-time options ---------------------------
-
-Be careful in setting #define values for numerical constants of type
-size_t. On some systems, literal values are not automatically extended
-to size_t precision unless they are explicitly casted. You can also
-use the symbolic values MAX_SIZE_T, SIZE_T_ONE, etc below.
-
-WIN32                    default: defined if _WIN32 defined
-  Defining WIN32 sets up defaults for MS environment and compilers.
-  Otherwise defaults are for unix. Beware that there seem to be some
-  cases where this malloc might not be a pure drop-in replacement for
-  Win32 malloc: Random-looking failures from Win32 GDI API's (eg;
-  SetDIBits()) may be due to bugs in some video driver implementations
-  when pixel buffers are malloc()ed, and the region spans more than
-  one VirtualAlloc()ed region. Because dlmalloc uses a small (64Kb)
-  default granularity, pixel buffers may straddle virtual allocation
-  regions more often than when using the Microsoft allocator.  You can
-  avoid this by using VirtualAlloc() and VirtualFree() for all pixel
-  buffers rather than using malloc().  If this is not possible,
-  recompile this malloc with a larger DEFAULT_GRANULARITY.
-
-MALLOC_ALIGNMENT         default: (size_t)8
-  Controls the minimum alignment for malloc'ed chunks.  It must be a
-  power of two and at least 8, even on machines for which smaller
-  alignments would suffice. It may be defined as larger than this
-  though. Note however that code and data structures are optimized for
-  the case of 8-byte alignment.
-
-MSPACES                  default: 0 (false)
-  If true, compile in support for independent allocation spaces.
-  This is only supported if HAVE_MMAP is true.
-
-ONLY_MSPACES             default: 0 (false)
-  If true, only compile in mspace versions, not regular versions.
-
-USE_LOCKS                default: 0 (false)
-  Causes each call to each public routine to be surrounded with
-  pthread or WIN32 mutex lock/unlock. (If set true, this can be
-  overridden on a per-mspace basis for mspace versions.) If set to a
-  non-zero value other than 1, locks are used, but their
-  implementation is left out, so lock functions must be supplied manually.
-
-USE_SPIN_LOCKS           default: 1 iff USE_LOCKS and on x86 using gcc or MSC
-  If true, uses custom spin locks for locking. This is currently
-  supported only for x86 platforms using gcc or recent MS compilers.
-  Otherwise, posix locks or win32 critical sections are used.
-
-FOOTERS                  default: 0
-  If true, provide extra checking and dispatching by placing
-  information in the footers of allocated chunks. This adds
-  space and time overhead.
-
-INSECURE                 default: 0
-  If true, omit checks for usage errors and heap space overwrites.
-
-USE_DL_PREFIX            default: NOT defined
-  Causes compiler to prefix all public routines with the string 'dl'.
-  This can be useful when you only want to use this malloc in one part
-  of a program, using your regular system malloc elsewhere.
-
-ABORT                    default: defined as abort()
-  Defines how to abort on failed checks.  On most systems, a failed
-  check cannot die with an "assert" or even print an informative
-  message, because the underlying print routines in turn call malloc,
-  which will fail again.  Generally, the best policy is to simply call
-  abort(). It's not very useful to do more than this because many
-  errors due to overwriting will show up as address faults (null, odd
-  addresses etc) rather than malloc-triggered checks, so will also
-  abort.  Also, most compilers know that abort() does not return, so
-  can better optimize code conditionally calling it.
-
-PROCEED_ON_ERROR           default: defined as 0 (false)
-  Controls whether detected bad addresses cause them to bypassed
-  rather than aborting. If set, detected bad arguments to free and
-  realloc are ignored. And all bookkeeping information is zeroed out
-  upon a detected overwrite of freed heap space, thus losing the
-  ability to ever return it from malloc again, but enabling the
-  application to proceed. If PROCEED_ON_ERROR is defined, the
-  static variable malloc_corruption_error_count is compiled in
-  and can be examined to see if errors have occurred. This option
-  generates slower code than the default abort policy.
-
-DEBUG                    default: NOT defined
-  The DEBUG setting is mainly intended for people trying to modify
-  this code or diagnose problems when porting to new platforms.
-  However, it may also be able to better isolate user errors than just
-  using runtime checks.  The assertions in the check routines spell
-  out in more detail the assumptions and invariants underlying the
-  algorithms.  The checking is fairly extensive, and will slow down
-  execution noticeably. Calling malloc_stats or mallinfo with DEBUG
-  set will attempt to check every non-mmapped allocated and free chunk
-  in the course of computing the summaries.
-
-ABORT_ON_ASSERT_FAILURE   default: defined as 1 (true)
-  Debugging assertion failures can be nearly impossible if your
-  version of the assert macro causes malloc to be called, which will
-  lead to a cascade of further failures, blowing the runtime stack.
-  ABORT_ON_ASSERT_FAILURE cause assertions failures to call abort(),
-  which will usually make debugging easier.
-
-MALLOC_FAILURE_ACTION     default: sets errno to ENOMEM, or no-op on win32
-  The action to take before "return 0" when malloc fails to be able to
-  return memory because there is none available.
-
-HAVE_MORECORE             default: 1 (true) unless win32 or ONLY_MSPACES
-  True if this system supports sbrk or an emulation of it.
-
-MORECORE                  default: sbrk
-  The name of the sbrk-style system routine to call to obtain more
-  memory.  See below for guidance on writing custom MORECORE
-  functions. The type of the argument to sbrk/MORECORE varies across
-  systems.  It cannot be size_t, because it supports negative
-  arguments, so it is normally the signed type of the same width as
-  size_t (sometimes declared as "intptr_t").  It doesn't much matter
-  though. Internally, we only call it with arguments less than half
-  the max value of a size_t, which should work across all reasonable
-  possibilities, although sometimes generating compiler warnings.
-
-MORECORE_CONTIGUOUS       default: 1 (true) if HAVE_MORECORE
-  If true, take advantage of fact that consecutive calls to MORECORE
-  with positive arguments always return contiguous increasing
-  addresses.  This is true of unix sbrk. It does not hurt too much to
-  set it true anyway, since malloc copes with non-contiguities.
-  Setting it false when definitely non-contiguous saves time
-  and possibly wasted space it would take to discover this though.
-
-MORECORE_CANNOT_TRIM      default: NOT defined
-  True if MORECORE cannot release space back to the system when given
-  negative arguments. This is generally necessary only if you are
-  using a hand-crafted MORECORE function that cannot handle negative
-  arguments.
-
-NO_SEGMENT_TRAVERSAL       default: 0
-  If non-zero, suppresses traversals of memory segments
-  returned by either MORECORE or CALL_MMAP. This disables
-  merging of segments that are contiguous, and selectively
-  releasing them to the OS if unused, but bounds execution times.
-
-HAVE_MMAP                 default: 1 (true)
-  True if this system supports mmap or an emulation of it.  If so, and
-  HAVE_MORECORE is not true, MMAP is used for all system
-  allocation. If set and HAVE_MORECORE is true as well, MMAP is
-  primarily used to directly allocate very large blocks. It is also
-  used as a backup strategy in cases where MORECORE fails to provide
-  space from system. Note: A single call to MUNMAP is assumed to be
-  able to unmap memory that may have be allocated using multiple calls
-  to MMAP, so long as they are adjacent.
-
-HAVE_MREMAP               default: 1 on linux, else 0
-  If true realloc() uses mremap() to re-allocate large blocks and
-  extend or shrink allocation spaces.
-
-MMAP_CLEARS               default: 1 except on WINCE.
-  True if mmap clears memory so calloc doesn't need to. This is true
-  for standard unix mmap using /dev/zero and on WIN32 except for WINCE.
-
-USE_BUILTIN_FFS            default: 0 (i.e., not used)
-  Causes malloc to use the builtin ffs() function to compute indices.
-  Some compilers may recognize and intrinsify ffs to be faster than the
-  supplied C version. Also, the case of x86 using gcc is special-cased
-  to an asm instruction, so is already as fast as it can be, and so
-  this setting has no effect. Similarly for Win32 under recent MS compilers.
-  (On most x86s, the asm version is only slightly faster than the C version.)
-
-malloc_getpagesize         default: derive from system includes, or 4096.
-  The system page size. To the extent possible, this malloc manages
-  memory from the system in page-size units.  This may be (and
-  usually is) a function rather than a constant. This is ignored
-  if WIN32, where page size is determined using getSystemInfo during
-  initialization.
-
-USE_DEV_RANDOM             default: 0 (i.e., not used)
-  Causes malloc to use /dev/random to initialize secure magic seed for
-  stamping footers. Otherwise, the current time is used.
-
-NO_MALLINFO                default: 0
-  If defined, don't compile "mallinfo". This can be a simple way
-  of dealing with mismatches between system declarations and
-  those in this file.
-
-MALLINFO_FIELD_TYPE        default: size_t
-  The type of the fields in the mallinfo struct. This was originally
-  defined as "int" in SVID etc, but is more usefully defined as
-  size_t. The value is used only if  HAVE_USR_INCLUDE_MALLOC_H is not set
-
-REALLOC_ZERO_BYTES_FREES    default: not defined
-  This should be set if a call to realloc with zero bytes should
-  be the same as a call to free. Some people think it should. Otherwise,
-  since this malloc returns a unique pointer for malloc(0), so does
-  realloc(p, 0).
-
-LACKS_UNISTD_H, LACKS_FCNTL_H, LACKS_SYS_PARAM_H, LACKS_SYS_MMAN_H
-LACKS_STRINGS_H, LACKS_STRING_H, LACKS_SYS_TYPES_H,  LACKS_ERRNO_H
-LACKS_STDLIB_H                default: NOT defined unless on WIN32
-  Define these if your system does not have these header files.
-  You might need to manually insert some of the declarations they provide.
-
-DEFAULT_GRANULARITY        default: page size if MORECORE_CONTIGUOUS,
-				system_info.dwAllocationGranularity in WIN32,
-				otherwise 64K.
-      Also settable using mallopt(M_GRANULARITY, x)
-  The unit for allocating and deallocating memory from the system.  On
-  most systems with contiguous MORECORE, there is no reason to
-  make this more than a page. However, systems with MMAP tend to
-  either require or encourage larger granularities.  You can increase
-  this value to prevent system allocation functions to be called so
-  often, especially if they are slow.  The value must be at least one
-  page and must be a power of two.  Setting to 0 causes initialization
-  to either page size or win32 region size.  (Note: In previous
-  versions of malloc, the equivalent of this option was called
-  "TOP_PAD")
-
-DEFAULT_TRIM_THRESHOLD    default: 2MB
-      Also settable using mallopt(M_TRIM_THRESHOLD, x)
-  The maximum amount of unused top-most memory to keep before
-  releasing via malloc_trim in free().  Automatic trimming is mainly
-  useful in long-lived programs using contiguous MORECORE.  Because
-  trimming via sbrk can be slow on some systems, and can sometimes be
-  wasteful (in cases where programs immediately afterward allocate
-  more large chunks) the value should be high enough so that your
-  overall system performance would improve by releasing this much
-  memory.  As a rough guide, you might set to a value close to the
-  average size of a process (program) running on your system.
-  Releasing this much memory would allow such a process to run in
-  memory.  Generally, it is worth tuning trim thresholds when a
-  program undergoes phases where several large chunks are allocated
-  and released in ways that can reuse each other's storage, perhaps
-  mixed with phases where there are no such chunks at all. The trim
-  value must be greater than page size to have any useful effect.  To
-  disable trimming completely, you can set to MAX_SIZE_T. Note that the trick
-  some people use of mallocing a huge space and then freeing it at
-  program startup, in an attempt to reserve system memory, doesn't
-  have the intended effect under automatic trimming, since that memory
-  will immediately be returned to the system.
-
-DEFAULT_MMAP_THRESHOLD       default: 256K
-      Also settable using mallopt(M_MMAP_THRESHOLD, x)
-  The request size threshold for using MMAP to directly service a
-  request. Requests of at least this size that cannot be allocated
-  using already-existing space will be serviced via mmap.  (If enough
-  normal freed space already exists it is used instead.)  Using mmap
-  segregates relatively large chunks of memory so that they can be
-  individually obtained and released from the host system. A request
-  serviced through mmap is never reused by any other request (at least
-  not directly; the system may just so happen to remap successive
-  requests to the same locations).  Segregating space in this way has
-  the benefits that: Mmapped space can always be individually released
-  back to the system, which helps keep the system level memory demands
-  of a long-lived program low.  Also, mapped memory doesn't become
-  `locked' between other chunks, as can happen with normally allocated
-  chunks, which means that even trimming via malloc_trim would not
-  release them.  However, it has the disadvantage that the space
-  cannot be reclaimed, consolidated, and then used to service later
-  requests, as happens with normal chunks.  The advantages of mmap
-  nearly always outweigh disadvantages for "large" chunks, but the
-  value of "large" may vary across systems.  The default is an
-  empirically derived value that works well in most systems. You can
-  disable mmap by setting to MAX_SIZE_T.
-
-MAX_RELEASE_CHECK_RATE   default: 4095 unless not HAVE_MMAP
-  The number of consolidated frees between checks to release
-  unused segments when freeing. When using non-contiguous segments,
-  especially with multiple mspaces, checking only for topmost space
-  doesn't always suffice to trigger trimming. To compensate for this,
-  free() will, with a period of MAX_RELEASE_CHECK_RATE (or the
-  current number of segments, if greater) try to release unused
-  segments to the OS when freeing chunks that result in
-  consolidation. The best value for this parameter is a compromise
-  between slowing down frees with relatively costly checks that
-  rarely trigger versus holding on to unused memory. To effectively
-  disable, set to MAX_SIZE_T. This may lead to a very slight speed
-  improvement at the expense of carrying around more memory.
-*/
-
-/* Version identifier to allow people to support multiple versions */
-#ifndef DLMALLOC_VERSION
-#define DLMALLOC_VERSION 20804
-#endif /* DLMALLOC_VERSION */
-
-#if defined(linux)
-#define _GNU_SOURCE 1
-#endif
-
-#ifndef WIN32
-#ifdef _WIN32
-#define WIN32 1
-#endif  /* _WIN32 */
-#ifdef _WIN32_WCE
-#define LACKS_FCNTL_H
-#define WIN32 1
-#endif /* _WIN32_WCE */
-#endif  /* WIN32 */
-#ifdef WIN32
-#define WIN32_LEAN_AND_MEAN
-#ifndef _WIN32_WINNT
-#define _WIN32_WINNT 0x603
-#endif
-#include <windows.h>
-#define HAVE_MMAP 1
-#define HAVE_MORECORE 0
-#define LACKS_UNISTD_H
-#define LACKS_SYS_PARAM_H
-#define LACKS_SYS_MMAN_H
-#define LACKS_STRING_H
-#define LACKS_STRINGS_H
-#define LACKS_SYS_TYPES_H
-#define LACKS_ERRNO_H
-#ifndef MALLOC_FAILURE_ACTION
-#define MALLOC_FAILURE_ACTION
-#endif /* MALLOC_FAILURE_ACTION */
-#ifdef _WIN32_WCE /* WINCE reportedly does not clear */
-#define MMAP_CLEARS 0
-#else
-#define MMAP_CLEARS 1
-#endif /* _WIN32_WCE */
-#endif  /* WIN32 */
-
-#if defined(DARWIN) || defined(_DARWIN)
-/* Mac OSX docs advise not to use sbrk; it seems better to use mmap */
-#ifndef HAVE_MORECORE
-#define HAVE_MORECORE 0
-#define HAVE_MMAP 1
-/* OSX allocators provide 16 byte alignment */
-#ifndef MALLOC_ALIGNMENT
-#define MALLOC_ALIGNMENT ((size_t)16U)
-#endif
-#endif  /* HAVE_MORECORE */
-#endif  /* DARWIN */
-
-#ifndef LACKS_SYS_TYPES_H
-#include <sys/types.h>  /* For size_t */
-#endif  /* LACKS_SYS_TYPES_H */
-
-/* The maximum possible size_t value has all bits set */
-#define MAX_SIZE_T           (~(size_t)0)
-
-#ifndef ONLY_MSPACES
-#define ONLY_MSPACES 0     /* define to a value */
-#else
-#define ONLY_MSPACES 1
-#endif  /* ONLY_MSPACES */
-#ifndef MSPACES
-#if ONLY_MSPACES
-#define MSPACES 1
-#else   /* ONLY_MSPACES */
-#define MSPACES 0
-#endif  /* ONLY_MSPACES */
-#endif  /* MSPACES */
-#ifndef MALLOC_ALIGNMENT
-#define MALLOC_ALIGNMENT ((size_t)8U)
-#endif  /* MALLOC_ALIGNMENT */
-#ifndef FOOTERS
-#define FOOTERS 0
-#endif  /* FOOTERS */
-#ifndef ABORT
-#define ABORT  abort()
-#endif  /* ABORT */
-#ifndef ABORT_ON_ASSERT_FAILURE
-#define ABORT_ON_ASSERT_FAILURE 1
-#endif  /* ABORT_ON_ASSERT_FAILURE */
-#ifndef PROCEED_ON_ERROR
-#define PROCEED_ON_ERROR 0
-#endif  /* PROCEED_ON_ERROR */
-#ifndef USE_LOCKS
-#define USE_LOCKS 0
-#endif  /* USE_LOCKS */
-#ifndef USE_SPIN_LOCKS
-#if USE_LOCKS && (defined(__GNUC__) && ((defined(__i386__) || defined(__x86_64__)))) || (defined(_MSC_VER) && _MSC_VER>=1310)
-#define USE_SPIN_LOCKS 1
-#else
-#define USE_SPIN_LOCKS 0
-#endif /* USE_LOCKS && ... */
-#endif /* USE_SPIN_LOCKS */
-#ifndef INSECURE
-#define INSECURE 0
-#endif  /* INSECURE */
-#ifndef HAVE_MMAP
-#define HAVE_MMAP 1
-#endif  /* HAVE_MMAP */
-#ifndef MMAP_CLEARS
-#define MMAP_CLEARS 1
-#endif  /* MMAP_CLEARS */
-#ifndef HAVE_MREMAP
-#ifdef linux
-#define HAVE_MREMAP 1
-#else   /* linux */
-#define HAVE_MREMAP 0
-#endif  /* linux */
-#endif  /* HAVE_MREMAP */
-#ifndef MALLOC_FAILURE_ACTION
-#define MALLOC_FAILURE_ACTION  errno = ENOMEM;
-#endif  /* MALLOC_FAILURE_ACTION */
-#ifndef HAVE_MORECORE
-#if ONLY_MSPACES
-#define HAVE_MORECORE 0
-#else   /* ONLY_MSPACES */
-#define HAVE_MORECORE 1
-#endif  /* ONLY_MSPACES */
-#endif  /* HAVE_MORECORE */
-#if !HAVE_MORECORE
-#define MORECORE_CONTIGUOUS 0
-#else   /* !HAVE_MORECORE */
-#define MORECORE_DEFAULT sbrk
-#ifndef MORECORE_CONTIGUOUS
-#define MORECORE_CONTIGUOUS 1
-#endif  /* MORECORE_CONTIGUOUS */
-#endif  /* HAVE_MORECORE */
-#ifndef DEFAULT_GRANULARITY
-#if (MORECORE_CONTIGUOUS || defined(WIN32))
-#define DEFAULT_GRANULARITY (0)  /* 0 means to compute in init_mparams */
-#else   /* MORECORE_CONTIGUOUS */
-#define DEFAULT_GRANULARITY ((size_t)64U * (size_t)1024U)
-#endif  /* MORECORE_CONTIGUOUS */
-#endif  /* DEFAULT_GRANULARITY */
-#ifndef DEFAULT_TRIM_THRESHOLD
-#ifndef MORECORE_CANNOT_TRIM
-#define DEFAULT_TRIM_THRESHOLD ((size_t)2U * (size_t)1024U * (size_t)1024U)
-#else   /* MORECORE_CANNOT_TRIM */
-#define DEFAULT_TRIM_THRESHOLD MAX_SIZE_T
-#endif  /* MORECORE_CANNOT_TRIM */
-#endif  /* DEFAULT_TRIM_THRESHOLD */
-#ifndef DEFAULT_MMAP_THRESHOLD
-#if HAVE_MMAP
-#define DEFAULT_MMAP_THRESHOLD ((size_t)256U * (size_t)1024U)
-#else   /* HAVE_MMAP */
-#define DEFAULT_MMAP_THRESHOLD MAX_SIZE_T
-#endif  /* HAVE_MMAP */
-#endif  /* DEFAULT_MMAP_THRESHOLD */
-#ifndef MAX_RELEASE_CHECK_RATE
-#if HAVE_MMAP
-#define MAX_RELEASE_CHECK_RATE 4095
-#else
-#define MAX_RELEASE_CHECK_RATE MAX_SIZE_T
-#endif /* HAVE_MMAP */
-#endif /* MAX_RELEASE_CHECK_RATE */
-#ifndef USE_BUILTIN_FFS
-#define USE_BUILTIN_FFS 0
-#endif  /* USE_BUILTIN_FFS */
-#ifndef USE_DEV_RANDOM
-#define USE_DEV_RANDOM 0
-#endif  /* USE_DEV_RANDOM */
-#ifndef NO_MALLINFO
-#define NO_MALLINFO 0
-#endif  /* NO_MALLINFO */
-#ifndef MALLINFO_FIELD_TYPE
-#define MALLINFO_FIELD_TYPE size_t
-#endif  /* MALLINFO_FIELD_TYPE */
-#ifndef NO_SEGMENT_TRAVERSAL
-#define NO_SEGMENT_TRAVERSAL 0
-#endif /* NO_SEGMENT_TRAVERSAL */
-
-/*
-  mallopt tuning options.  SVID/XPG defines four standard parameter
-  numbers for mallopt, normally defined in malloc.h.  None of these
-  are used in this malloc, so setting them has no effect. But this
-  malloc does support the following options.
-*/
-
-#define M_TRIM_THRESHOLD     (-1)
-#define M_GRANULARITY        (-2)
-#define M_MMAP_THRESHOLD     (-3)
-
-/* ------------------------ Mallinfo declarations ------------------------ */
-
-#if !NO_MALLINFO
-/*
-  This version of malloc supports the standard SVID/XPG mallinfo
-  routine that returns a struct containing usage properties and
-  statistics. It should work on any system that has a
-  /usr/include/malloc.h defining struct mallinfo.  The main
-  declaration needed is the mallinfo struct that is returned (by-copy)
-  by mallinfo().  The malloinfo struct contains a bunch of fields that
-  are not even meaningful in this version of malloc.  These fields are
-  are instead filled by mallinfo() with other numbers that might be of
-  interest.
-
-  HAVE_USR_INCLUDE_MALLOC_H should be set if you have a
-  /usr/include/malloc.h file that includes a declaration of struct
-  mallinfo.  If so, it is included; else a compliant version is
-  declared below.  These must be precisely the same for mallinfo() to
-  work.  The original SVID version of this struct, defined on most
-  systems with mallinfo, declares all fields as ints. But some others
-  define as unsigned long. If your system defines the fields using a
-  type of different width than listed here, you MUST #include your
-  system version and #define HAVE_USR_INCLUDE_MALLOC_H.
-*/
-
-/* #define HAVE_USR_INCLUDE_MALLOC_H */
-
-#ifdef HAVE_USR_INCLUDE_MALLOC_H
-#include "/usr/include/malloc.h"
-#else /* HAVE_USR_INCLUDE_MALLOC_H */
-#ifndef STRUCT_MALLINFO_DECLARED
-#define STRUCT_MALLINFO_DECLARED 1
-struct mallinfo {
-  MALLINFO_FIELD_TYPE arena;    /* non-mmapped space allocated from system */
-  MALLINFO_FIELD_TYPE ordblks;  /* number of free chunks */
-  MALLINFO_FIELD_TYPE smblks;   /* always 0 */
-  MALLINFO_FIELD_TYPE hblks;    /* always 0 */
-  MALLINFO_FIELD_TYPE hblkhd;   /* space in mmapped regions */
-  MALLINFO_FIELD_TYPE usmblks;  /* maximum total allocated space */
-  MALLINFO_FIELD_TYPE fsmblks;  /* always 0 */
-  MALLINFO_FIELD_TYPE uordblks; /* total allocated space */
-  MALLINFO_FIELD_TYPE fordblks; /* total free space */
-  MALLINFO_FIELD_TYPE keepcost; /* releasable (via malloc_trim) space */
-};
-#endif /* STRUCT_MALLINFO_DECLARED */
-#endif /* HAVE_USR_INCLUDE_MALLOC_H */
-#endif /* NO_MALLINFO */
-
-/*
-  Try to persuade compilers to inline. The most critical functions for
-  inlining are defined as macros, so these aren't used for them.
-*/
-
-#ifdef __MINGW64_VERSION_MAJOR
-#undef FORCEINLINE
-#endif
-#ifndef FORCEINLINE
-  #if defined(__GNUC__)
-#define FORCEINLINE __inline __attribute__ ((always_inline))
-  #elif defined(_MSC_VER)
-    #define FORCEINLINE __forceinline
-  #endif
-#endif
-#ifndef NOINLINE
-  #if defined(__GNUC__)
-    #define NOINLINE __attribute__ ((noinline))
-  #elif defined(_MSC_VER)
-    #define NOINLINE __declspec(noinline)
-  #else
-    #define NOINLINE
-  #endif
-#endif
-
-#ifdef __cplusplus
-extern "C" {
-#ifndef FORCEINLINE
- #define FORCEINLINE inline
-#endif
-#endif /* __cplusplus */
-#ifndef FORCEINLINE
- #define FORCEINLINE
-#endif
-
-#if !ONLY_MSPACES
-
-/* ------------------- Declarations of public routines ------------------- */
-
-#ifndef USE_DL_PREFIX
-#define dlcalloc               calloc
-#define dlfree                 free
-#define dlmalloc               malloc
-#define dlmemalign             memalign
-#define dlrealloc              realloc
-#define dlvalloc               valloc
-#define dlpvalloc              pvalloc
-#define dlmallinfo             mallinfo
-#define dlmallopt              mallopt
-#define dlmalloc_trim          malloc_trim
-#define dlmalloc_stats         malloc_stats
-#define dlmalloc_usable_size   malloc_usable_size
-#define dlmalloc_footprint     malloc_footprint
-#define dlmalloc_max_footprint malloc_max_footprint
-#define dlindependent_calloc   independent_calloc
-#define dlindependent_comalloc independent_comalloc
-#endif /* USE_DL_PREFIX */
-
-
-/*
-  malloc(size_t n)
-  Returns a pointer to a newly allocated chunk of at least n bytes, or
-  null if no space is available, in which case errno is set to ENOMEM
-  on ANSI C systems.
-
-  If n is zero, malloc returns a minimum-sized chunk. (The minimum
-  size is 16 bytes on most 32bit systems, and 32 bytes on 64bit
-  systems.)  Note that size_t is an unsigned type, so calls with
-  arguments that would be negative if signed are interpreted as
-  requests for huge amounts of space, which will often fail. The
-  maximum supported value of n differs across systems, but is in all
-  cases less than the maximum representable value of a size_t.
-*/
-void* dlmalloc(size_t);
-
-/*
-  free(void* p)
-  Releases the chunk of memory pointed to by p, that had been previously
-  allocated using malloc or a related routine such as realloc.
-  It has no effect if p is null. If p was not malloced or already
-  freed, free(p) will by default cause the current program to abort.
-*/
-void  dlfree(void*);
-
-/*
-  calloc(size_t n_elements, size_t element_size);
-  Returns a pointer to n_elements * element_size bytes, with all locations
-  set to zero.
-*/
-void* dlcalloc(size_t, size_t);
-
-/*
-  realloc(void* p, size_t n)
-  Returns a pointer to a chunk of size n that contains the same data
-  as does chunk p up to the minimum of (n, p's size) bytes, or null
-  if no space is available.
-
-  The returned pointer may or may not be the same as p. The algorithm
-  prefers extending p in most cases when possible, otherwise it
-  employs the equivalent of a malloc-copy-free sequence.
-
-  If p is null, realloc is equivalent to malloc.
-
-  If space is not available, realloc returns null, errno is set (if on
-  ANSI) and p is NOT freed.
-
-  if n is for fewer bytes than already held by p, the newly unused
-  space is lopped off and freed if possible.  realloc with a size
-  argument of zero (re)allocates a minimum-sized chunk.
-
-  The old unix realloc convention of allowing the last-free'd chunk
-  to be used as an argument to realloc is not supported.
-*/
-
-void* dlrealloc(void*, size_t);
-
-/*
-  memalign(size_t alignment, size_t n);
-  Returns a pointer to a newly allocated chunk of n bytes, aligned
-  in accord with the alignment argument.
-
-  The alignment argument should be a power of two. If the argument is
-  not a power of two, the nearest greater power is used.
-  8-byte alignment is guaranteed by normal malloc calls, so don't
-  bother calling memalign with an argument of 8 or less.
-
-  Overreliance on memalign is a sure way to fragment space.
-*/
-void* dlmemalign(size_t, size_t);
-
-/*
-  valloc(size_t n);
-  Equivalent to memalign(pagesize, n), where pagesize is the page
-  size of the system. If the pagesize is unknown, 4096 is used.
-*/
-void* dlvalloc(size_t);
-
-/*
-  mallopt(int parameter_number, int parameter_value)
-  Sets tunable parameters The format is to provide a
-  (parameter-number, parameter-value) pair.  mallopt then sets the
-  corresponding parameter to the argument value if it can (i.e., so
-  long as the value is meaningful), and returns 1 if successful else
-  0.  To workaround the fact that mallopt is specified to use int,
-  not size_t parameters, the value -1 is specially treated as the
-  maximum unsigned size_t value.
-
-  SVID/XPG/ANSI defines four standard param numbers for mallopt,
-  normally defined in malloc.h.  None of these are use in this malloc,
-  so setting them has no effect. But this malloc also supports other
-  options in mallopt. See below for details.  Briefly, supported
-  parameters are as follows (listed defaults are for "typical"
-  configurations).
-
-  Symbol            param #  default    allowed param values
-  M_TRIM_THRESHOLD     -1   2*1024*1024   any   (-1 disables)
-  M_GRANULARITY        -2     page size   any power of 2 >= page size
-  M_MMAP_THRESHOLD     -3      256*1024   any   (or 0 if no MMAP support)
-*/
-int dlmallopt(int, int);
-
-/*
-  malloc_footprint();
-  Returns the number of bytes obtained from the system.  The total
-  number of bytes allocated by malloc, realloc etc., is less than this
-  value. Unlike mallinfo, this function returns only a precomputed
-  result, so can be called frequently to monitor memory consumption.
-  Even if locks are otherwise defined, this function does not use them,
-  so results might not be up to date.
-*/
-size_t dlmalloc_footprint(void);
-
-/*
-  malloc_max_footprint();
-  Returns the maximum number of bytes obtained from the system. This
-  value will be greater than current footprint if deallocated space
-  has been reclaimed by the system. The peak number of bytes allocated
-  by malloc, realloc etc., is less than this value. Unlike mallinfo,
-  this function returns only a precomputed result, so can be called
-  frequently to monitor memory consumption.  Even if locks are
-  otherwise defined, this function does not use them, so results might
-  not be up to date.
-*/
-size_t dlmalloc_max_footprint(void);
-
-#if !NO_MALLINFO
-/*
-  mallinfo()
-  Returns (by copy) a struct containing various summary statistics:
-
-  arena:     current total non-mmapped bytes allocated from system
-  ordblks:   the number of free chunks
-  smblks:    always zero.
-  hblks:     current number of mmapped regions
-  hblkhd:    total bytes held in mmapped regions
-  usmblks:   the maximum total allocated space. This will be greater
-		than current total if trimming has occurred.
-  fsmblks:   always zero
-  uordblks:  current total allocated space (normal or mmapped)
-  fordblks:  total free space
-  keepcost:  the maximum number of bytes that could ideally be released
-	       back to system via malloc_trim. ("ideally" means that
-	       it ignores page restrictions etc.)
-
-  Because these fields are ints, but internal bookkeeping may
-  be kept as longs, the reported values may wrap around zero and
-  thus be inaccurate.
-*/
-struct mallinfo dlmallinfo(void);
-#endif /* NO_MALLINFO */
-
-/*
-  independent_calloc(size_t n_elements, size_t element_size, void* chunks[]);
-
-  independent_calloc is similar to calloc, but instead of returning a
-  single cleared space, it returns an array of pointers to n_elements
-  independent elements that can hold contents of size elem_size, each
-  of which starts out cleared, and can be independently freed,
-  realloc'ed etc. The elements are guaranteed to be adjacently
-  allocated (this is not guaranteed to occur with multiple callocs or
-  mallocs), which may also improve cache locality in some
-  applications.
-
-  The "chunks" argument is optional (i.e., may be null, which is
-  probably the most typical usage). If it is null, the returned array
-  is itself dynamically allocated and should also be freed when it is
-  no longer needed. Otherwise, the chunks array must be of at least
-  n_elements in length. It is filled in with the pointers to the
-  chunks.
-
-  In either case, independent_calloc returns this pointer array, or
-  null if the allocation failed.  If n_elements is zero and "chunks"
-  is null, it returns a chunk representing an array with zero elements
-  (which should be freed if not wanted).
-
-  Each element must be individually freed when it is no longer
-  needed. If you'd like to instead be able to free all at once, you
-  should instead use regular calloc and assign pointers into this
-  space to represent elements.  (In this case though, you cannot
-  independently free elements.)
-
-  independent_calloc simplifies and speeds up implementations of many
-  kinds of pools.  It may also be useful when constructing large data
-  structures that initially have a fixed number of fixed-sized nodes,
-  but the number is not known at compile time, and some of the nodes
-  may later need to be freed. For example:
-
-  struct Node { int item; struct Node* next; };
-
-  struct Node* build_list() {
-    struct Node** pool;
-    int n = read_number_of_nodes_needed();
-    if (n <= 0) return 0;
-    pool = (struct Node**)(independent_calloc(n, sizeof(struct Node), 0);
-    if (pool == 0) die();
-    // organize into a linked list...
-    struct Node* first = pool[0];
-    for (i = 0; i < n-1; ++i)
-      pool[i]->next = pool[i+1];
-    free(pool);     // Can now free the array (or not, if it is needed later)
-    return first;
-  }
-*/
-void** dlindependent_calloc(size_t, size_t, void**);
-
-/*
-  independent_comalloc(size_t n_elements, size_t sizes[], void* chunks[]);
-
-  independent_comalloc allocates, all at once, a set of n_elements
-  chunks with sizes indicated in the "sizes" array.    It returns
-  an array of pointers to these elements, each of which can be
-  independently freed, realloc'ed etc. The elements are guaranteed to
-  be adjacently allocated (this is not guaranteed to occur with
-  multiple callocs or mallocs), which may also improve cache locality
-  in some applications.
-
-  The "chunks" argument is optional (i.e., may be null). If it is null
-  the returned array is itself dynamically allocated and should also
-  be freed when it is no longer needed. Otherwise, the chunks array
-  must be of at least n_elements in length. It is filled in with the
-  pointers to the chunks.
-
-  In either case, independent_comalloc returns this pointer array, or
-  null if the allocation failed.  If n_elements is zero and chunks is
-  null, it returns a chunk representing an array with zero elements
-  (which should be freed if not wanted).
-
-  Each element must be individually freed when it is no longer
-  needed. If you'd like to instead be able to free all at once, you
-  should instead use a single regular malloc, and assign pointers at
-  particular offsets in the aggregate space. (In this case though, you
-  cannot independently free elements.)
-
-  independent_comallac differs from independent_calloc in that each
-  element may have a different size, and also that it does not
-  automatically clear elements.
-
-  independent_comalloc can be used to speed up allocation in cases
-  where several structs or objects must always be allocated at the
-  same time.  For example:
-
-  struct Head { ... }
-  struct Foot { ... }
-
-  void send_message(char* msg) {
-    int msglen = strlen(msg);
-    size_t sizes[3] = { sizeof(struct Head), msglen, sizeof(struct Foot) };
-    void* chunks[3];
-    if (independent_comalloc(3, sizes, chunks) == 0)
-      die();
-    struct Head* head = (struct Head*)(chunks[0]);
-    char*        body = (char*)(chunks[1]);
-    struct Foot* foot = (struct Foot*)(chunks[2]);
-    // ...
-  }
-
-  In general though, independent_comalloc is worth using only for
-  larger values of n_elements. For small values, you probably won't
-  detect enough difference from series of malloc calls to bother.
-
-  Overuse of independent_comalloc can increase overall memory usage,
-  since it cannot reuse existing noncontiguous small chunks that
-  might be available for some of the elements.
-*/
-void** dlindependent_comalloc(size_t, size_t*, void**);
-
-
-/*
-  pvalloc(size_t n);
-  Equivalent to valloc(minimum-page-that-holds(n)), that is,
-  round up n to nearest pagesize.
- */
-void*  dlpvalloc(size_t);
-
-/*
-  malloc_trim(size_t pad);
-
-  If possible, gives memory back to the system (via negative arguments
-  to sbrk) if there is unused memory at the `high' end of the malloc
-  pool or in unused MMAP segments. You can call this after freeing
-  large blocks of memory to potentially reduce the system-level memory
-  requirements of a program. However, it cannot guarantee to reduce
-  memory. Under some allocation patterns, some large free blocks of
-  memory will be locked between two used chunks, so they cannot be
-  given back to the system.
-
-  The `pad' argument to malloc_trim represents the amount of free
-  trailing space to leave untrimmed. If this argument is zero, only
-  the minimum amount of memory to maintain internal data structures
-  will be left. Non-zero arguments can be supplied to maintain enough
-  trailing space to service future expected allocations without having
-  to re-obtain memory from the system.
-
-  Malloc_trim returns 1 if it actually released any memory, else 0.
-*/
-int  dlmalloc_trim(size_t);
-
-/*
-  malloc_stats();
-  Prints on stderr the amount of space obtained from the system (both
-  via sbrk and mmap), the maximum amount (which may be more than
-  current if malloc_trim and/or munmap got called), and the current
-  number of bytes allocated via malloc (or realloc, etc) but not yet
-  freed. Note that this is the number of bytes allocated, not the
-  number requested. It will be larger than the number requested
-  because of alignment and bookkeeping overhead. Because it includes
-  alignment wastage as being in use, this figure may be greater than
-  zero even when no user-level chunks are allocated.
-
-  The reported current and maximum system memory can be inaccurate if
-  a program makes other calls to system memory allocation functions
-  (normally sbrk) outside of malloc.
-
-  malloc_stats prints only the most commonly interesting statistics.
-  More information can be obtained by calling mallinfo.
-*/
-void  dlmalloc_stats(void);
-
-#endif /* ONLY_MSPACES */
-
-/*
-  malloc_usable_size(void* p);
-
-  Returns the number of bytes you can actually use in
-  an allocated chunk, which may be more than you requested (although
-  often not) due to alignment and minimum size constraints.
-  You can use this many bytes without worrying about
-  overwriting other allocated objects. This is not a particularly great
-  programming practice. malloc_usable_size can be more useful in
-  debugging and assertions, for example:
-
-  p = malloc(n);
-  assert(malloc_usable_size(p) >= 256);
-*/
-size_t dlmalloc_usable_size(void*);
-
-
-#if MSPACES
-
-/*
-  mspace is an opaque type representing an independent
-  region of space that supports mspace_malloc, etc.
-*/
-typedef void* mspace;
-
-/*
-  create_mspace creates and returns a new independent space with the
-  given initial capacity, or, if 0, the default granularity size.  It
-  returns null if there is no system memory available to create the
-  space.  If argument locked is non-zero, the space uses a separate
-  lock to control access. The capacity of the space will grow
-  dynamically as needed to service mspace_malloc requests.  You can
-  control the sizes of incremental increases of this space by
-  compiling with a different DEFAULT_GRANULARITY or dynamically
-  setting with mallopt(M_GRANULARITY, value).
-*/
-mspace create_mspace(size_t capacity, int locked);
-
-/*
-  destroy_mspace destroys the given space, and attempts to return all
-  of its memory back to the system, returning the total number of
-  bytes freed. After destruction, the results of access to all memory
-  used by the space become undefined.
-*/
-size_t destroy_mspace(mspace msp);
-
-/*
-  create_mspace_with_base uses the memory supplied as the initial base
-  of a new mspace. Part (less than 128*sizeof(size_t) bytes) of this
-  space is used for bookkeeping, so the capacity must be at least this
-  large. (Otherwise 0 is returned.) When this initial space is
-  exhausted, additional memory will be obtained from the system.
-  Destroying this space will deallocate all additionally allocated
-  space (if possible) but not the initial base.
-*/
-mspace create_mspace_with_base(void* base, size_t capacity, int locked);
-
-/*
-  mspace_mmap_large_chunks controls whether requests for large chunks
-  are allocated in their own mmapped regions, separate from others in
-  this mspace. By default this is enabled, which reduces
-  fragmentation. However, such chunks are not necessarily released to
-  the system upon destroy_mspace.  Disabling by setting to false may
-  increase fragmentation, but avoids leakage when relying on
-  destroy_mspace to release all memory allocated using this space.
-*/
-int mspace_mmap_large_chunks(mspace msp, int enable);
-
-
-/*
-  mspace_malloc behaves as malloc, but operates within
-  the given space.
-*/
-void* mspace_malloc(mspace msp, size_t bytes);
-
-/*
-  mspace_free behaves as free, but operates within
-  the given space.
-
-  If compiled with FOOTERS==1, mspace_free is not actually needed.
-  free may be called instead of mspace_free because freed chunks from
-  any space are handled by their originating spaces.
-*/
-void mspace_free(mspace msp, void* mem);
-
-/*
-  mspace_realloc behaves as realloc, but operates within
-  the given space.
-
-  If compiled with FOOTERS==1, mspace_realloc is not actually
-  needed.  realloc may be called instead of mspace_realloc because
-  realloced chunks from any space are handled by their originating
-  spaces.
-*/
-void* mspace_realloc(mspace msp, void* mem, size_t newsize);
-
-/*
-  mspace_calloc behaves as calloc, but operates within
-  the given space.
-*/
-void* mspace_calloc(mspace msp, size_t n_elements, size_t elem_size);
-
-/*
-  mspace_memalign behaves as memalign, but operates within
-  the given space.
-*/
-void* mspace_memalign(mspace msp, size_t alignment, size_t bytes);
-
-/*
-  mspace_independent_calloc behaves as independent_calloc, but
-  operates within the given space.
-*/
-void** mspace_independent_calloc(mspace msp, size_t n_elements,
-				 size_t elem_size, void* chunks[]);
-
-/*
-  mspace_independent_comalloc behaves as independent_comalloc, but
-  operates within the given space.
-*/
-void** mspace_independent_comalloc(mspace msp, size_t n_elements,
-				   size_t sizes[], void* chunks[]);
-
-/*
-  mspace_footprint() returns the number of bytes obtained from the
-  system for this space.
-*/
-size_t mspace_footprint(mspace msp);
-
-/*
-  mspace_max_footprint() returns the peak number of bytes obtained from the
-  system for this space.
-*/
-size_t mspace_max_footprint(mspace msp);
-
-
-#if !NO_MALLINFO
-/*
-  mspace_mallinfo behaves as mallinfo, but reports properties of
-  the given space.
-*/
-struct mallinfo mspace_mallinfo(mspace msp);
-#endif /* NO_MALLINFO */
-
-/*
-  malloc_usable_size(void* p) behaves the same as malloc_usable_size;
-*/
-  size_t mspace_usable_size(void* mem);
-
-/*
-  mspace_malloc_stats behaves as malloc_stats, but reports
-  properties of the given space.
-*/
-void mspace_malloc_stats(mspace msp);
-
-/*
-  mspace_trim behaves as malloc_trim, but
-  operates within the given space.
-*/
-int mspace_trim(mspace msp, size_t pad);
-
-/*
-  An alias for mallopt.
-*/
-int mspace_mallopt(int, int);
-
-#endif /* MSPACES */
-
-#ifdef __cplusplus
-};  /* end of extern "C" */
-#endif /* __cplusplus */
-
-/*
-  ========================================================================
-  To make a fully customizable malloc.h header file, cut everything
-  above this line, put into file malloc.h, edit to suit, and #include it
-  on the next line, as well as in programs that use this malloc.
-  ========================================================================
-*/
-
-/* #include "malloc.h" */
-
-/*------------------------------ internal #includes ---------------------- */
-
-#ifdef WIN32
-#ifndef __GNUC__
-#pragma warning( disable : 4146 ) /* no "unsigned" warnings */
-#endif
-#endif /* WIN32 */
-
-#include <stdio.h>       /* for printing in malloc_stats */
-
-#ifndef LACKS_ERRNO_H
-#include <errno.h>       /* for MALLOC_FAILURE_ACTION */
-#endif /* LACKS_ERRNO_H */
-#if FOOTERS
-#include <time.h>        /* for magic initialization */
-#endif /* FOOTERS */
-#ifndef LACKS_STDLIB_H
-#include <stdlib.h>      /* for abort() */
-#endif /* LACKS_STDLIB_H */
-#ifdef DEBUG
-#if ABORT_ON_ASSERT_FAILURE
-#define assert(x) if(!(x)) ABORT
-#else /* ABORT_ON_ASSERT_FAILURE */
-#include <assert.h>
-#endif /* ABORT_ON_ASSERT_FAILURE */
-#else  /* DEBUG */
-#ifndef assert
-#define assert(x)
-#endif
-#define DEBUG 0
-#endif /* DEBUG */
-#ifndef LACKS_STRING_H
-#include <string.h>      /* for memset etc */
-#endif  /* LACKS_STRING_H */
-#if USE_BUILTIN_FFS
-#ifndef LACKS_STRINGS_H
-#include <strings.h>     /* for ffs */
-#endif /* LACKS_STRINGS_H */
-#endif /* USE_BUILTIN_FFS */
-#if HAVE_MMAP
-#ifndef LACKS_SYS_MMAN_H
-#include <sys/mman.h>    /* for mmap */
-#endif /* LACKS_SYS_MMAN_H */
-#ifndef LACKS_FCNTL_H
-#include <fcntl.h>
-#endif /* LACKS_FCNTL_H */
-#endif /* HAVE_MMAP */
-#ifndef LACKS_UNISTD_H
-#include <unistd.h>     /* for sbrk, sysconf */
-#else /* LACKS_UNISTD_H */
-#if !defined(__FreeBSD__) && !defined(__OpenBSD__) && !defined(__NetBSD__)
-extern void*     sbrk(ptrdiff_t);
-#endif /* FreeBSD etc */
-#endif /* LACKS_UNISTD_H */
-
-/* Declarations for locking */
-#if USE_LOCKS
-#ifndef WIN32
-#include <pthread.h>
-#if defined (__SVR4) && defined (__sun)  /* solaris */
-#include <thread.h>
-#endif /* solaris */
-#else
-#ifndef _M_AMD64
-/* These are already defined on AMD64 builds */
-#ifdef __cplusplus
-extern "C" {
-#endif /* __cplusplus */
-#ifndef __MINGW32__
-LONG __cdecl _InterlockedCompareExchange(LONG volatile *Dest, LONG Exchange, LONG Comp);
-LONG __cdecl _InterlockedExchange(LONG volatile *Target, LONG Value);
-#endif
-#ifdef __cplusplus
-}
-#endif /* __cplusplus */
-#endif /* _M_AMD64 */
-#ifndef __MINGW32__
-#pragma intrinsic (_InterlockedCompareExchange)
-#pragma intrinsic (_InterlockedExchange)
-#else
-  /* --[ start GCC compatibility ]----------------------------------------------
-   * Compatibility <intrin_x86.h> header for GCC -- GCC equivalents of intrinsic
-   * Microsoft Visual C++ functions. Originally developed for the ReactOS
-   * (<http://www.reactos.org/>) and TinyKrnl (<http://www.tinykrnl.org/>)
-   * projects.
-   *
-   * Copyright (c) 2006 KJK::Hyperion <hackbunny@reactos.com>
-   *
-   * Permission is hereby granted, free of charge, to any person obtaining a
-   * copy of this software and associated documentation files (the "Software"),
-   * to deal in the Software without restriction, including without limitation
-   * the rights to use, copy, modify, merge, publish, distribute, sublicense,
-   * and/or sell copies of the Software, and to permit persons to whom the
-   * Software is furnished to do so, subject to the following conditions:
-   *
-   * The above copyright notice and this permission notice shall be included in
-   * all copies or substantial portions of the Software.
-   *
-   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-   * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-   * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
-   * DEALINGS IN THE SOFTWARE.
-   */
-
-  /*** Atomic operations ***/
-  #if (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) > 40100
-    #undef _ReadWriteBarrier
-    #define _ReadWriteBarrier() __sync_synchronize()
-  #else
-    static __inline__ __attribute__((always_inline)) long __sync_lock_test_and_set(volatile long * const Target, const long Value)
-    {
-      long res;
-      __asm__ __volatile__("xchg%z0 %2, %0" : "=g" (*(Target)), "=r" (res) : "1" (Value));
-      return res;
-    }
-    static void __inline__ __attribute__((always_inline)) _MemoryBarrier(void)
-    {
-      __asm__ __volatile__("" : : : "memory");
-    }
-    #define _ReadWriteBarrier() _MemoryBarrier()
-  #endif
-  /* BUGBUG: GCC only supports full barriers */
-  static __inline__ __attribute__((always_inline)) long _InterlockedExchange(volatile long * const Target, const long Value)
-  {
-    /* NOTE: __sync_lock_test_and_set would be an acquire barrier, so we force a full barrier */
-    _ReadWriteBarrier();
-    return __sync_lock_test_and_set(Target, Value);
-  }
-  /* --[ end GCC compatibility ]---------------------------------------------- */
-#endif
-#define interlockedcompareexchange _InterlockedCompareExchange
-#define interlockedexchange _InterlockedExchange
-#endif /* Win32 */
-#endif /* USE_LOCKS */
-
-/* Declarations for bit scanning on win32 */
-#if defined(_MSC_VER) && _MSC_VER>=1300
-#ifndef BitScanForward	/* Try to avoid pulling in WinNT.h */
-#ifdef __cplusplus
-extern "C" {
-#endif /* __cplusplus */
-unsigned char _BitScanForward(unsigned long *index, unsigned long mask);
-unsigned char _BitScanReverse(unsigned long *index, unsigned long mask);
-#ifdef __cplusplus
-}
-#endif /* __cplusplus */
-
-#define BitScanForward _BitScanForward
-#define BitScanReverse _BitScanReverse
-#pragma intrinsic(_BitScanForward)
-#pragma intrinsic(_BitScanReverse)
-#endif /* BitScanForward */
-#endif /* defined(_MSC_VER) && _MSC_VER>=1300 */
-
-#ifndef WIN32
-#ifndef malloc_getpagesize
-#  ifdef _SC_PAGESIZE         /* some SVR4 systems omit an underscore */
-#    ifndef _SC_PAGE_SIZE
-#      define _SC_PAGE_SIZE _SC_PAGESIZE
-#    endif
-#  endif
-#  ifdef _SC_PAGE_SIZE
-#    define malloc_getpagesize sysconf(_SC_PAGE_SIZE)
-#  else
-#    if defined(BSD) || defined(DGUX) || defined(HAVE_GETPAGESIZE)
-       extern size_t getpagesize();
-#      define malloc_getpagesize getpagesize()
-#    else
-#      ifdef WIN32 /* use supplied emulation of getpagesize */
-#        define malloc_getpagesize getpagesize()
-#      else
-#        ifndef LACKS_SYS_PARAM_H
-#          include <sys/param.h>
-#        endif
-#        ifdef EXEC_PAGESIZE
-#          define malloc_getpagesize EXEC_PAGESIZE
-#        else
-#          ifdef NBPG
-#            ifndef CLSIZE
-#              define malloc_getpagesize NBPG
-#            else
-#              define malloc_getpagesize (NBPG * CLSIZE)
-#            endif
-#          else
-#            ifdef NBPC
-#              define malloc_getpagesize NBPC
-#            else
-#              ifdef PAGESIZE
-#                define malloc_getpagesize PAGESIZE
-#              else /* just guess */
-#                define malloc_getpagesize ((size_t)4096U)
-#              endif
-#            endif
-#          endif
-#        endif
-#      endif
-#    endif
-#  endif
-#endif
-#endif
-
-
-
-/* ------------------- size_t and alignment properties -------------------- */
-
-/* The byte and bit size of a size_t */
-#define SIZE_T_SIZE         (sizeof(size_t))
-#define SIZE_T_BITSIZE      (sizeof(size_t) << 3)
-
-/* Some constants coerced to size_t */
-/* Annoying but necessary to avoid errors on some platforms */
-#define SIZE_T_ZERO         ((size_t)0)
-#define SIZE_T_ONE          ((size_t)1)
-#define SIZE_T_TWO          ((size_t)2)
-#define SIZE_T_FOUR         ((size_t)4)
-#define TWO_SIZE_T_SIZES    (SIZE_T_SIZE<<1)
-#define FOUR_SIZE_T_SIZES   (SIZE_T_SIZE<<2)
-#define SIX_SIZE_T_SIZES    (FOUR_SIZE_T_SIZES+TWO_SIZE_T_SIZES)
-#define HALF_MAX_SIZE_T     (MAX_SIZE_T / 2U)
-
-/* The bit mask value corresponding to MALLOC_ALIGNMENT */
-#define CHUNK_ALIGN_MASK    (MALLOC_ALIGNMENT - SIZE_T_ONE)
-
-/* True if address a has acceptable alignment */
-#define is_aligned(A)       (((size_t)((A)) & (CHUNK_ALIGN_MASK)) == 0)
-
-/* the number of bytes to offset an address to align it */
-#define align_offset(A)\
- ((((size_t)(A) & CHUNK_ALIGN_MASK) == 0)? 0 :\
-  ((MALLOC_ALIGNMENT - ((size_t)(A) & CHUNK_ALIGN_MASK)) & CHUNK_ALIGN_MASK))
-
-/* -------------------------- MMAP preliminaries ------------------------- */
-
-/*
-   If HAVE_MORECORE or HAVE_MMAP are false, we just define calls and
-   checks to fail so compiler optimizer can delete code rather than
-   using so many "#if"s.
-*/
-
-
-/* MORECORE and MMAP must return MFAIL on failure */
-#define MFAIL                ((void*)(MAX_SIZE_T))
-#define CMFAIL               ((char*)(MFAIL)) /* defined for convenience */
-
-#if HAVE_MMAP
-
-#ifndef WIN32
-#define MUNMAP_DEFAULT(a, s)  munmap((a), (s))
-#define MMAP_PROT            (PROT_READ|PROT_WRITE)
-#if !defined(MAP_ANONYMOUS) && defined(MAP_ANON)
-#define MAP_ANONYMOUS        MAP_ANON
-#endif /* MAP_ANON */
-#ifdef MAP_ANONYMOUS
-#define MMAP_FLAGS           (MAP_PRIVATE|MAP_ANONYMOUS)
-#define MMAP_DEFAULT(s)       mmap(0, (s), MMAP_PROT, MMAP_FLAGS, -1, 0)
-#else /* MAP_ANONYMOUS */
-/*
-   Nearly all versions of mmap support MAP_ANONYMOUS, so the following
-   is unlikely to be needed, but is supplied just in case.
-*/
-#define MMAP_FLAGS           (MAP_PRIVATE)
-static int dev_zero_fd = -1; /* Cached file descriptor for /dev/zero. */
-#define MMAP_DEFAULT(s) ((dev_zero_fd < 0) ? \
-	   (dev_zero_fd = open("/dev/zero", O_RDWR), \
-	    mmap(0, (s), MMAP_PROT, MMAP_FLAGS, dev_zero_fd, 0)) : \
-	    mmap(0, (s), MMAP_PROT, MMAP_FLAGS, dev_zero_fd, 0))
-#endif /* MAP_ANONYMOUS */
-
-#define DIRECT_MMAP_DEFAULT(s) MMAP_DEFAULT(s)
-
-#else /* WIN32 */
-
-/* Win32 MMAP via VirtualAlloc */
-static FORCEINLINE void* win32mmap(size_t size) {
-  void* ptr = VirtualAlloc(0, size, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE);
-  return (ptr != 0)? ptr: MFAIL;
-}
-
-/* For direct MMAP, use MEM_TOP_DOWN to minimize interference */
-static FORCEINLINE void* win32direct_mmap(size_t size) {
-  void* ptr = VirtualAlloc(0, size, MEM_RESERVE|MEM_COMMIT|MEM_TOP_DOWN,
-			   PAGE_READWRITE);
-  return (ptr != 0)? ptr: MFAIL;
-}
-
-/* This function supports releasing coalesced segments */
-static FORCEINLINE int win32munmap(void* ptr, size_t size) {
-  MEMORY_BASIC_INFORMATION minfo;
-  char* cptr = (char*)ptr;
-  while (size) {
-    if (VirtualQuery(cptr, &minfo, sizeof(minfo)) == 0)
-      return -1;
-    if (minfo.BaseAddress != cptr || minfo.AllocationBase != cptr ||
-	minfo.State != MEM_COMMIT || minfo.RegionSize > size)
-      return -1;
-    if (VirtualFree(cptr, 0, MEM_RELEASE) == 0)
-      return -1;
-    cptr += minfo.RegionSize;
-    size -= minfo.RegionSize;
-  }
-  return 0;
-}
-
-#define MMAP_DEFAULT(s)             win32mmap(s)
-#define MUNMAP_DEFAULT(a, s)        win32munmap((a), (s))
-#define DIRECT_MMAP_DEFAULT(s)      win32direct_mmap(s)
-#endif /* WIN32 */
-#endif /* HAVE_MMAP */
-
-#if HAVE_MREMAP
-#ifndef WIN32
-#define MREMAP_DEFAULT(addr, osz, nsz, mv) mremap((addr), (osz), (nsz), (mv))
-#endif /* WIN32 */
-#endif /* HAVE_MREMAP */
-
-
-/**
- * Define CALL_MORECORE
- */
-#if HAVE_MORECORE
-    #ifdef MORECORE
-	#define CALL_MORECORE(S)    MORECORE(S)
-    #else  /* MORECORE */
-	#define CALL_MORECORE(S)    MORECORE_DEFAULT(S)
-    #endif /* MORECORE */
-#else  /* HAVE_MORECORE */
-    #define CALL_MORECORE(S)        MFAIL
-#endif /* HAVE_MORECORE */
-
-/**
- * Define CALL_MMAP/CALL_MUNMAP/CALL_DIRECT_MMAP
- */
-#if HAVE_MMAP
-    #define IS_MMAPPED_BIT          (SIZE_T_ONE)
-    #define USE_MMAP_BIT            (SIZE_T_ONE)
-
-    #ifdef MMAP
-	#define CALL_MMAP(s)        MMAP(s)
-    #else /* MMAP */
-	#define CALL_MMAP(s)        MMAP_DEFAULT(s)
-    #endif /* MMAP */
-    #ifdef MUNMAP
-	#define CALL_MUNMAP(a, s)   MUNMAP((a), (s))
-    #else /* MUNMAP */
-	#define CALL_MUNMAP(a, s)   MUNMAP_DEFAULT((a), (s))
-    #endif /* MUNMAP */
-    #ifdef DIRECT_MMAP
-	#define CALL_DIRECT_MMAP(s) DIRECT_MMAP(s)
-    #else /* DIRECT_MMAP */
-	#define CALL_DIRECT_MMAP(s) DIRECT_MMAP_DEFAULT(s)
-    #endif /* DIRECT_MMAP */
-#else  /* HAVE_MMAP */
-    #define IS_MMAPPED_BIT          (SIZE_T_ZERO)
-    #define USE_MMAP_BIT            (SIZE_T_ZERO)
-
-    #define MMAP(s)                 MFAIL
-    #define MUNMAP(a, s)            (-1)
-    #define DIRECT_MMAP(s)          MFAIL
-    #define CALL_DIRECT_MMAP(s)     DIRECT_MMAP(s)
-    #define CALL_MMAP(s)            MMAP(s)
-    #define CALL_MUNMAP(a, s)       MUNMAP((a), (s))
-#endif /* HAVE_MMAP */
-
-/**
- * Define CALL_MREMAP
- */
-#if HAVE_MMAP && HAVE_MREMAP
-    #ifdef MREMAP
-	#define CALL_MREMAP(addr, osz, nsz, mv) MREMAP((addr), (osz), (nsz), (mv))
-    #else /* MREMAP */
-	#define CALL_MREMAP(addr, osz, nsz, mv) MREMAP_DEFAULT((addr), (osz), (nsz), (mv))
-    #endif /* MREMAP */
-#else  /* HAVE_MMAP && HAVE_MREMAP */
-    #define CALL_MREMAP(addr, osz, nsz, mv)     MFAIL
-#endif /* HAVE_MMAP && HAVE_MREMAP */
-
-/* mstate bit set if contiguous morecore disabled or failed */
-#define USE_NONCONTIGUOUS_BIT (4U)
-
-/* segment bit set in create_mspace_with_base */
-#define EXTERN_BIT            (8U)
-
-
-/* --------------------------- Lock preliminaries ------------------------ */
-
-/*
-  When locks are defined, there is one global lock, plus
-  one per-mspace lock.
-
-  The global lock_ensures that mparams.magic and other unique
-  mparams values are initialized only once. It also protects
-  sequences of calls to MORECORE.  In many cases sys_alloc requires
-  two calls, that should not be interleaved with calls by other
-  threads.  This does not protect against direct calls to MORECORE
-  by other threads not using this lock, so there is still code to
-  cope the best we can on interference.
-
-  Per-mspace locks surround calls to malloc, free, etc.  To enable use
-  in layered extensions, per-mspace locks are reentrant.
-
-  Because lock-protected regions generally have bounded times, it is
-  OK to use the supplied simple spinlocks in the custom versions for
-  x86.
-
-  If USE_LOCKS is > 1, the definitions of lock routines here are
-  bypassed, in which case you will need to define at least
-  INITIAL_LOCK, ACQUIRE_LOCK, RELEASE_LOCK and possibly TRY_LOCK
-  (which is not used in this malloc, but commonly needed in
-  extensions.)
-*/
-
-#if USE_LOCKS == 1
-
-#if USE_SPIN_LOCKS
-#ifndef WIN32
-
-/* Custom pthread-style spin locks on x86 and x64 for gcc */
-struct pthread_mlock_t {
-  volatile unsigned int l;
-  volatile unsigned int c;
-  volatile pthread_t threadid;
-};
-#define MLOCK_T struct        pthread_mlock_t
-#define CURRENT_THREAD        pthread_self()
-#define INITIAL_LOCK(sl)      (memset(sl, 0, sizeof(MLOCK_T)), 0)
-#define ACQUIRE_LOCK(sl)      pthread_acquire_lock(sl)
-#define RELEASE_LOCK(sl)      pthread_release_lock(sl)
-#define TRY_LOCK(sl)          pthread_try_lock(sl)
-#define SPINS_PER_YIELD       63
-
-static MLOCK_T malloc_global_mutex = { 0, 0, 0};
-
-static FORCEINLINE int pthread_acquire_lock (MLOCK_T *sl) {
-  int spins = 0;
-  volatile unsigned int* lp = &sl->l;
-  for (;;) {
-    if (*lp != 0) {
-      if (sl->threadid == CURRENT_THREAD) {
-	++sl->c;
-	return 0;
-      }
-    }
-    else {
-      /* place args to cmpxchgl in locals to evade oddities in some gccs */
-      int cmp = 0;
-      int val = 1;
-      int ret;
-      __asm__ __volatile__  ("lock; cmpxchgl %1, %2"
-			     : "=a" (ret)
-			     : "r" (val), "m" (*(lp)), "0"(cmp)
-			     : "memory", "cc");
-      if (!ret) {
-	assert(!sl->threadid);
-	sl->c = 1;
-	sl->threadid = CURRENT_THREAD;
-	return 0;
-      }
-      if ((++spins & SPINS_PER_YIELD) == 0) {
-#if defined (__SVR4) && defined (__sun) /* solaris */
-	thr_yield();
-#else
-#if defined(__linux__) || defined(__FreeBSD__) || defined(__APPLE__)
-	sched_yield();
-#else  /* no-op yield on unknown systems */
-	;
-#endif /* __linux__ || __FreeBSD__ || __APPLE__ */
-#endif /* solaris */
-      }
-    }
-  }
-}
-
-static FORCEINLINE void pthread_release_lock (MLOCK_T *sl) {
-  assert(sl->l != 0);
-  assert(sl->threadid == CURRENT_THREAD);
-  if (--sl->c == 0) {
-    volatile unsigned int* lp = &sl->l;
-    int prev = 0;
-    int ret;
-    sl->threadid = 0;
-    __asm__ __volatile__ ("lock; xchgl %0, %1"
-			  : "=r" (ret)
-			  : "m" (*(lp)), "0"(prev)
-			  : "memory");
-  }
-}
-
-static FORCEINLINE int pthread_try_lock (MLOCK_T *sl) {
-  volatile unsigned int* lp = &sl->l;
-  if (*lp != 0) {
-      if (sl->threadid == CURRENT_THREAD) {
-	++sl->c;
-	return 1;
-      }
-  }
-  else {
-    int cmp = 0;
-    int val = 1;
-    int ret;
-    __asm__ __volatile__  ("lock; cmpxchgl %1, %2"
-			   : "=a" (ret)
-			   : "r" (val), "m" (*(lp)), "0"(cmp)
-			   : "memory", "cc");
-    if (!ret) {
-      assert(!sl->threadid);
-      sl->c = 1;
-      sl->threadid = CURRENT_THREAD;
-      return 1;
-    }
-  }
-  return 0;
-}
-
-
-#else /* WIN32 */
-/* Custom win32-style spin locks on x86 and x64 for MSC */
-struct win32_mlock_t
-{
-  volatile long l;
-  volatile unsigned int c;
-  volatile long threadid;
-};
-
-static inline int return_0(int i) { return 0; }
-#define MLOCK_T               struct win32_mlock_t
-#define CURRENT_THREAD        win32_getcurrentthreadid()
-#define INITIAL_LOCK(sl)      (memset(sl, 0, sizeof(MLOCK_T)), return_0(0))
-#define ACQUIRE_LOCK(sl)      win32_acquire_lock(sl)
-#define RELEASE_LOCK(sl)      win32_release_lock(sl)
-#define TRY_LOCK(sl)          win32_try_lock(sl)
-#define SPINS_PER_YIELD       63
-
-static MLOCK_T malloc_global_mutex = { 0, 0, 0};
-
-static FORCEINLINE long win32_getcurrentthreadid(void) {
-#ifdef _MSC_VER
-#if defined(_M_IX86)
-  long *threadstruct=(long *)__readfsdword(0x18);
-  long threadid=threadstruct[0x24/sizeof(long)];
-  return threadid;
-#elif defined(_M_X64)
-  /* todo */
-  return GetCurrentThreadId();
-#else
-  return GetCurrentThreadId();
-#endif
-#else
-  return GetCurrentThreadId();
-#endif
-}
-
-static FORCEINLINE int win32_acquire_lock (MLOCK_T *sl) {
-  int spins = 0;
-  for (;;) {
-    if (sl->l != 0) {
-      if (sl->threadid == CURRENT_THREAD) {
-	++sl->c;
-	return 0;
-      }
-    }
-    else {
-      if (!interlockedexchange(&sl->l, 1)) {
-	assert(!sl->threadid);
-		sl->c=CURRENT_THREAD;
-	sl->threadid = CURRENT_THREAD;
-	sl->c = 1;
-	return 0;
-      }
-    }
-    if ((++spins & SPINS_PER_YIELD) == 0)
-      SleepEx(0, FALSE);
-  }
-}
-
-static FORCEINLINE void win32_release_lock (MLOCK_T *sl) {
-  assert(sl->threadid == CURRENT_THREAD);
-  assert(sl->l != 0);
-  if (--sl->c == 0) {
-    sl->threadid = 0;
-    interlockedexchange (&sl->l, 0);
-  }
-}
-
-static FORCEINLINE int win32_try_lock (MLOCK_T *sl) {
-  if(sl->l != 0) {
-      if (sl->threadid == CURRENT_THREAD) {
-	++sl->c;
-	return 1;
-      }
-  }
-  else {
-    if (!interlockedexchange(&sl->l, 1)){
-      assert(!sl->threadid);
-      sl->threadid = CURRENT_THREAD;
-      sl->c = 1;
-      return 1;
-    }
-  }
-  return 0;
-}
-
-#endif /* WIN32 */
-#else /* USE_SPIN_LOCKS */
-
-#ifndef WIN32
-/* pthreads-based locks */
-
-#define MLOCK_T               pthread_mutex_t
-#define CURRENT_THREAD        pthread_self()
-#define INITIAL_LOCK(sl)      pthread_init_lock(sl)
-#define ACQUIRE_LOCK(sl)      pthread_mutex_lock(sl)
-#define RELEASE_LOCK(sl)      pthread_mutex_unlock(sl)
-#define TRY_LOCK(sl)          (!pthread_mutex_trylock(sl))
-
-static MLOCK_T malloc_global_mutex = PTHREAD_MUTEX_INITIALIZER;
-
-/* Cope with old-style linux recursive lock initialization by adding */
-/* skipped internal declaration from pthread.h */
-#ifdef linux
-#ifndef PTHREAD_MUTEX_RECURSIVE
-extern int pthread_mutexattr_setkind_np __P ((pthread_mutexattr_t *__attr,
-					   int __kind));
-#define PTHREAD_MUTEX_RECURSIVE PTHREAD_MUTEX_RECURSIVE_NP
-#define pthread_mutexattr_settype(x,y) pthread_mutexattr_setkind_np(x,y)
-#endif
-#endif
-
-static int pthread_init_lock (MLOCK_T *sl) {
-  pthread_mutexattr_t attr;
-  if (pthread_mutexattr_init(&attr)) return 1;
-  if (pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE)) return 1;
-  if (pthread_mutex_init(sl, &attr)) return 1;
-  if (pthread_mutexattr_destroy(&attr)) return 1;
-  return 0;
-}
-
-#else /* WIN32 */
-/* Win32 critical sections */
-#define MLOCK_T               CRITICAL_SECTION
-#define CURRENT_THREAD        GetCurrentThreadId()
-#define INITIAL_LOCK(s)       (!InitializeCriticalSectionAndSpinCount((s), 0x80000000|4000))
-#define ACQUIRE_LOCK(s)       (EnterCriticalSection(s), 0)
-#define RELEASE_LOCK(s)       LeaveCriticalSection(s)
-#define TRY_LOCK(s)           TryEnterCriticalSection(s)
-#define NEED_GLOBAL_LOCK_INIT
-
-static MLOCK_T malloc_global_mutex;
-static volatile long malloc_global_mutex_status;
-
-/* Use spin loop to initialize global lock */
-static void init_malloc_global_mutex() {
-  for (;;) {
-    long stat = malloc_global_mutex_status;
-    if (stat > 0)
-      return;
-    /* transition to < 0 while initializing, then to > 0) */
-    if (stat == 0 &&
-	interlockedcompareexchange(&malloc_global_mutex_status, -1, 0) == 0) {
-      InitializeCriticalSection(&malloc_global_mutex);
-      interlockedexchange(&malloc_global_mutex_status,1);
-      return;
-    }
-    SleepEx(0, FALSE);
-  }
-}
-
-#endif /* WIN32 */
-#endif /* USE_SPIN_LOCKS */
-#endif /* USE_LOCKS == 1 */
-
-/* -----------------------  User-defined locks ------------------------ */
-
-#if USE_LOCKS > 1
-/* Define your own lock implementation here */
-/* #define INITIAL_LOCK(sl)  ... */
-/* #define ACQUIRE_LOCK(sl)  ... */
-/* #define RELEASE_LOCK(sl)  ... */
-/* #define TRY_LOCK(sl) ... */
-/* static MLOCK_T malloc_global_mutex = ... */
-#endif /* USE_LOCKS > 1 */
-
-/* -----------------------  Lock-based state ------------------------ */
-
-#if USE_LOCKS
-#define USE_LOCK_BIT               (2U)
-#else  /* USE_LOCKS */
-#define USE_LOCK_BIT               (0U)
-#define INITIAL_LOCK(l)
-#endif /* USE_LOCKS */
-
-#if USE_LOCKS
-#define ACQUIRE_MALLOC_GLOBAL_LOCK()  ACQUIRE_LOCK(&malloc_global_mutex);
-#define RELEASE_MALLOC_GLOBAL_LOCK()  RELEASE_LOCK(&malloc_global_mutex);
-#else  /* USE_LOCKS */
-#define ACQUIRE_MALLOC_GLOBAL_LOCK()
-#define RELEASE_MALLOC_GLOBAL_LOCK()
-#endif /* USE_LOCKS */
-
-
-/* -----------------------  Chunk representations ------------------------ */
-
-/*
-  (The following includes lightly edited explanations by Colin Plumb.)
-
-  The malloc_chunk declaration below is misleading (but accurate and
-  necessary).  It declares a "view" into memory allowing access to
-  necessary fields at known offsets from a given base.
-
-  Chunks of memory are maintained using a `boundary tag' method as
-  originally described by Knuth.  (See the paper by Paul Wilson
-  ftp://ftp.cs.utexas.edu/pub/garbage/allocsrv.ps for a survey of such
-  techniques.)  Sizes of free chunks are stored both in the front of
-  each chunk and at the end.  This makes consolidating fragmented
-  chunks into bigger chunks fast.  The head fields also hold bits
-  representing whether chunks are free or in use.
-
-  Here are some pictures to make it clearer.  They are "exploded" to
-  show that the state of a chunk can be thought of as extending from
-  the high 31 bits of the head field of its header through the
-  prev_foot and PINUSE_BIT bit of the following chunk header.
-
-  A chunk that's in use looks like:
-
-   chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-	   | Size of previous chunk (if P = 0)                             |
-	   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-	 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |P|
-	 | Size of this chunk                                         1| +-+
-   mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-	 |                                                               |
-	 +-                                                             -+
-	 |                                                               |
-	 +-                                                             -+
-	 |                                                               :
-	 +-      size - sizeof(size_t) available payload bytes          -+
-	 :                                                               |
- chunk-> +-                                                             -+
-	 |                                                               |
-	 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |1|
-       | Size of next chunk (may or may not be in use)               | +-+
- mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-
-    And if it's free, it looks like this:
-
-   chunk-> +-                                                             -+
-	   | User payload (must be in use, or we would have merged!)       |
-	   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-	 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |P|
-	 | Size of this chunk                                         0| +-+
-   mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-	 | Next pointer                                                  |
-	 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-	 | Prev pointer                                                  |
-	 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-	 |                                                               :
-	 +-      size - sizeof(struct chunk) unused bytes               -+
-	 :                                                               |
- chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-	 | Size of this chunk                                            |
-	 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |0|
-       | Size of next chunk (must be in use, or we would have merged)| +-+
- mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-       |                                                               :
-       +- User payload                                                -+
-       :                                                               |
-       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-								     |0|
-								     +-+
-  Note that since we always merge adjacent free chunks, the chunks
-  adjacent to a free chunk must be in use.
-
-  Given a pointer to a chunk (which can be derived trivially from the
-  payload pointer) we can, in O(1) time, find out whether the adjacent
-  chunks are free, and if so, unlink them from the lists that they
-  are on and merge them with the current chunk.
-
-  Chunks always begin on even word boundaries, so the mem portion
-  (which is returned to the user) is also on an even word boundary, and
-  thus at least double-word aligned.
-
-  The P (PINUSE_BIT) bit, stored in the unused low-order bit of the
-  chunk size (which is always a multiple of two words), is an in-use
-  bit for the *previous* chunk.  If that bit is *clear*, then the
-  word before the current chunk size contains the previous chunk
-  size, and can be used to find the front of the previous chunk.
-  The very first chunk allocated always has this bit set, preventing
-  access to non-existent (or non-owned) memory. If pinuse is set for
-  any given chunk, then you CANNOT determine the size of the
-  previous chunk, and might even get a memory addressing fault when
-  trying to do so.
-
-  The C (CINUSE_BIT) bit, stored in the unused second-lowest bit of
-  the chunk size redundantly records whether the current chunk is
-  inuse. This redundancy enables usage checks within free and realloc,
-  and reduces indirection when freeing and consolidating chunks.
-
-  Each freshly allocated chunk must have both cinuse and pinuse set.
-  That is, each allocated chunk borders either a previously allocated
-  and still in-use chunk, or the base of its memory arena. This is
-  ensured by making all allocations from the `lowest' part of any
-  found chunk.  Further, no free chunk physically borders another one,
-  so each free chunk is known to be preceded and followed by either
-  inuse chunks or the ends of memory.
-
-  Note that the `foot' of the current chunk is actually represented
-  as the prev_foot of the NEXT chunk. This makes it easier to
-  deal with alignments etc but can be very confusing when trying
-  to extend or adapt this code.
-
-  The exceptions to all this are
-
-     1. The special chunk `top' is the top-most available chunk (i.e.,
-	the one bordering the end of available memory). It is treated
-	specially.  Top is never included in any bin, is used only if
-	no other chunk is available, and is released back to the
-	system if it is very large (see M_TRIM_THRESHOLD).  In effect,
-	the top chunk is treated as larger (and thus less well
-	fitting) than any other available chunk.  The top chunk
-	doesn't update its trailing size field since there is no next
-	contiguous chunk that would have to index off it. However,
-	space is still allocated for it (TOP_FOOT_SIZE) to enable
-	separation or merging when space is extended.
-
-     3. Chunks allocated via mmap, which have the lowest-order bit
-	(IS_MMAPPED_BIT) set in their prev_foot fields, and do not set
-	PINUSE_BIT in their head fields.  Because they are allocated
-	one-by-one, each must carry its own prev_foot field, which is
-	also used to hold the offset this chunk has within its mmapped
-	region, which is needed to preserve alignment. Each mmapped
-	chunk is trailed by the first two fields of a fake next-chunk
-	for sake of usage checks.
-
-*/
-
-struct malloc_chunk {
-  size_t               prev_foot;  /* Size of previous chunk (if free).  */
-  size_t               head;       /* Size and inuse bits. */
-  struct malloc_chunk* fd;         /* double links -- used only if free. */
-  struct malloc_chunk* bk;
-};
-
-typedef struct malloc_chunk  mchunk;
-typedef struct malloc_chunk* mchunkptr;
-typedef struct malloc_chunk* sbinptr;  /* The type of bins of chunks */
-typedef unsigned int bindex_t;         /* Described below */
-typedef unsigned int binmap_t;         /* Described below */
-typedef unsigned int flag_t;           /* The type of various bit flag sets */
-
-/* ------------------- Chunks sizes and alignments ----------------------- */
-
-#define MCHUNK_SIZE         (sizeof(mchunk))
-
-#if FOOTERS
-#define CHUNK_OVERHEAD      (TWO_SIZE_T_SIZES)
-#else /* FOOTERS */
-#define CHUNK_OVERHEAD      (SIZE_T_SIZE)
-#endif /* FOOTERS */
-
-/* MMapped chunks need a second word of overhead ... */
-#define MMAP_CHUNK_OVERHEAD (TWO_SIZE_T_SIZES)
-/* ... and additional padding for fake next-chunk at foot */
-#define MMAP_FOOT_PAD       (FOUR_SIZE_T_SIZES)
-
-/* The smallest size we can malloc is an aligned minimal chunk */
-#define MIN_CHUNK_SIZE\
-  ((MCHUNK_SIZE + CHUNK_ALIGN_MASK) & ~CHUNK_ALIGN_MASK)
-
-/* conversion from malloc headers to user pointers, and back */
-#define chunk2mem(p)        ((void*)((char*)(p)       + TWO_SIZE_T_SIZES))
-#define mem2chunk(mem)      ((mchunkptr)((char*)(mem) - TWO_SIZE_T_SIZES))
-/* chunk associated with aligned address A */
-#define align_as_chunk(A)   (mchunkptr)((A) + align_offset(chunk2mem(A)))
-
-/* Bounds on request (not chunk) sizes. */
-#define MAX_REQUEST         ((-MIN_CHUNK_SIZE) << 2)
-#define MIN_REQUEST         (MIN_CHUNK_SIZE - CHUNK_OVERHEAD - SIZE_T_ONE)
-
-/* pad request bytes into a usable size */
-#define pad_request(req) \
-   (((req) + CHUNK_OVERHEAD + CHUNK_ALIGN_MASK) & ~CHUNK_ALIGN_MASK)
-
-/* pad request, checking for minimum (but not maximum) */
-#define request2size(req) \
-  (((req) < MIN_REQUEST)? MIN_CHUNK_SIZE : pad_request(req))
-
-
-/* ------------------ Operations on head and foot fields ----------------- */
-
-/*
-  The head field of a chunk is or'ed with PINUSE_BIT when previous
-  adjacent chunk in use, and or'ed with CINUSE_BIT if this chunk is in
-  use. If the chunk was obtained with mmap, the prev_foot field has
-  IS_MMAPPED_BIT set, otherwise holding the offset of the base of the
-  mmapped region to the base of the chunk.
-
-  FLAG4_BIT is not used by this malloc, but might be useful in extensions.
-*/
-
-#define PINUSE_BIT          (SIZE_T_ONE)
-#define CINUSE_BIT          (SIZE_T_TWO)
-#define FLAG4_BIT           (SIZE_T_FOUR)
-#define INUSE_BITS          (PINUSE_BIT|CINUSE_BIT)
-#define FLAG_BITS           (PINUSE_BIT|CINUSE_BIT|FLAG4_BIT)
-
-/* Head value for fenceposts */
-#define FENCEPOST_HEAD      (INUSE_BITS|SIZE_T_SIZE)
-
-/* extraction of fields from head words */
-#define cinuse(p)           ((p)->head & CINUSE_BIT)
-#define pinuse(p)           ((p)->head & PINUSE_BIT)
-#define chunksize(p)        ((p)->head & ~(FLAG_BITS))
-
-#define clear_pinuse(p)     ((p)->head &= ~PINUSE_BIT)
-#define clear_cinuse(p)     ((p)->head &= ~CINUSE_BIT)
-
-/* Treat space at ptr +/- offset as a chunk */
-#define chunk_plus_offset(p, s)  ((mchunkptr)(((char*)(p)) + (s)))
-#define chunk_minus_offset(p, s) ((mchunkptr)(((char*)(p)) - (s)))
-
-/* Ptr to next or previous physical malloc_chunk. */
-#define next_chunk(p) ((mchunkptr)( ((char*)(p)) + ((p)->head & ~FLAG_BITS)))
-#define prev_chunk(p) ((mchunkptr)( ((char*)(p)) - ((p)->prev_foot) ))
-
-/* extract next chunk's pinuse bit */
-#define next_pinuse(p)  ((next_chunk(p)->head) & PINUSE_BIT)
-
-/* Get/set size at footer */
-#define get_foot(p, s)  (((mchunkptr)((char*)(p) + (s)))->prev_foot)
-#define set_foot(p, s)  (((mchunkptr)((char*)(p) + (s)))->prev_foot = (s))
-
-/* Set size, pinuse bit, and foot */
-#define set_size_and_pinuse_of_free_chunk(p, s)\
-  ((p)->head = (s|PINUSE_BIT), set_foot(p, s))
-
-/* Set size, pinuse bit, foot, and clear next pinuse */
-#define set_free_with_pinuse(p, s, n)\
-  (clear_pinuse(n), set_size_and_pinuse_of_free_chunk(p, s))
-
-#define is_mmapped(p)\
-  (!((p)->head & PINUSE_BIT) && ((p)->prev_foot & IS_MMAPPED_BIT))
-
-/* Get the internal overhead associated with chunk p */
-#define overhead_for(p)\
- (is_mmapped(p)? MMAP_CHUNK_OVERHEAD : CHUNK_OVERHEAD)
-
-/* Return true if malloced space is not necessarily cleared */
-#if MMAP_CLEARS
-#define calloc_must_clear(p) (!is_mmapped(p))
-#else /* MMAP_CLEARS */
-#define calloc_must_clear(p) (1)
-#endif /* MMAP_CLEARS */
-
-/* ---------------------- Overlaid data structures ----------------------- */
-
-/*
-  When chunks are not in use, they are treated as nodes of either
-  lists or trees.
-
-  "Small"  chunks are stored in circular doubly-linked lists, and look
-  like this:
-
-    chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-	    |             Size of previous chunk                            |
-	    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-    `head:' |             Size of chunk, in bytes                         |P|
-      mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-	    |             Forward pointer to next chunk in list             |
-	    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-	    |             Back pointer to previous chunk in list            |
-	    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-	    |             Unused space (may be 0 bytes long)                .
-	    .                                                               .
-	    .                                                               |
-nextchunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-    `foot:' |             Size of chunk, in bytes                           |
-	    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-
-  Larger chunks are kept in a form of bitwise digital trees (aka
-  tries) keyed on chunksizes.  Because malloc_tree_chunks are only for
-  free chunks greater than 256 bytes, their size doesn't impose any
-  constraints on user chunk sizes.  Each node looks like:
-
-    chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-	    |             Size of previous chunk                            |
-	    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-    `head:' |             Size of chunk, in bytes                         |P|
-      mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-	    |             Forward pointer to next chunk of same size        |
-	    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-	    |             Back pointer to previous chunk of same size       |
-	    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-	    |             Pointer to left child (child[0])                  |
-	    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-	    |             Pointer to right child (child[1])                 |
-	    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-	    |             Pointer to parent                                 |
-	    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-	    |             bin index of this chunk                           |
-	    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-	    |             Unused space                                      .
-	    .                                                               |
-nextchunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-    `foot:' |             Size of chunk, in bytes                           |
-	    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-
-  Each tree holding treenodes is a tree of unique chunk sizes.  Chunks
-  of the same size are arranged in a circularly-linked list, with only
-  the oldest chunk (the next to be used, in our FIFO ordering)
-  actually in the tree.  (Tree members are distinguished by a non-null
-  parent pointer.)  If a chunk with the same size as an existing node
-  is inserted, it is linked off the existing node using pointers that
-  work in the same way as fd/bk pointers of small chunks.
-
-  Each tree contains a power of 2 sized range of chunk sizes (the
-  smallest is 0x100 <= x < 0x180), which is divided in half at each
-  tree level, with the chunks in the smaller half of the range (0x100
-  <= x < 0x140 for the top nose) in the left subtree and the larger
-  half (0x140 <= x < 0x180) in the right subtree.  This is, of course,
-  done by inspecting individual bits.
-
-  Using these rules, each node's left subtree contains all smaller
-  sizes than its right subtree.  However, the node at the root of each
-  subtree has no particular ordering relationship to either.  (The
-  dividing line between the subtree sizes is based on trie relation.)
-  If we remove the last chunk of a given size from the interior of the
-  tree, we need to replace it with a leaf node.  The tree ordering
-  rules permit a node to be replaced by any leaf below it.
-
-  The smallest chunk in a tree (a common operation in a best-fit
-  allocator) can be found by walking a path to the leftmost leaf in
-  the tree.  Unlike a usual binary tree, where we follow left child
-  pointers until we reach a null, here we follow the right child
-  pointer any time the left one is null, until we reach a leaf with
-  both child pointers null. The smallest chunk in the tree will be
-  somewhere along that path.
-
-  The worst case number of steps to add, find, or remove a node is
-  bounded by the number of bits differentiating chunks within
-  bins. Under current bin calculations, this ranges from 6 up to 21
-  (for 32 bit sizes) or up to 53 (for 64 bit sizes). The typical case
-  is of course much better.
-*/
-
-struct malloc_tree_chunk {
-  /* The first four fields must be compatible with malloc_chunk */
-  size_t                    prev_foot;
-  size_t                    head;
-  struct malloc_tree_chunk* fd;
-  struct malloc_tree_chunk* bk;
-
-  struct malloc_tree_chunk* child[2];
-  struct malloc_tree_chunk* parent;
-  bindex_t                  index;
-};
-
-typedef struct malloc_tree_chunk  tchunk;
-typedef struct malloc_tree_chunk* tchunkptr;
-typedef struct malloc_tree_chunk* tbinptr; /* The type of bins of trees */
-
-/* A little helper macro for trees */
-#define leftmost_child(t) ((t)->child[0] != 0? (t)->child[0] : (t)->child[1])
-
-/* ----------------------------- Segments -------------------------------- */
-
-/*
-  Each malloc space may include non-contiguous segments, held in a
-  list headed by an embedded malloc_segment record representing the
-  top-most space. Segments also include flags holding properties of
-  the space. Large chunks that are directly allocated by mmap are not
-  included in this list. They are instead independently created and
-  destroyed without otherwise keeping track of them.
-
-  Segment management mainly comes into play for spaces allocated by
-  MMAP.  Any call to MMAP might or might not return memory that is
-  adjacent to an existing segment.  MORECORE normally contiguously
-  extends the current space, so this space is almost always adjacent,
-  which is simpler and faster to deal with. (This is why MORECORE is
-  used preferentially to MMAP when both are available -- see
-  sys_alloc.)  When allocating using MMAP, we don't use any of the
-  hinting mechanisms (inconsistently) supported in various
-  implementations of unix mmap, or distinguish reserving from
-  committing memory. Instead, we just ask for space, and exploit
-  contiguity when we get it.  It is probably possible to do
-  better than this on some systems, but no general scheme seems
-  to be significantly better.
-
-  Management entails a simpler variant of the consolidation scheme
-  used for chunks to reduce fragmentation -- new adjacent memory is
-  normally prepended or appended to an existing segment. However,
-  there are limitations compared to chunk consolidation that mostly
-  reflect the fact that segment processing is relatively infrequent
-  (occurring only when getting memory from system) and that we
-  don't expect to have huge numbers of segments:
-
-  * Segments are not indexed, so traversal requires linear scans.  (It
-    would be possible to index these, but is not worth the extra
-    overhead and complexity for most programs on most platforms.)
-  * New segments are only appended to old ones when holding top-most
-    memory; if they cannot be prepended to others, they are held in
-    different segments.
-
-  Except for the top-most segment of an mstate, each segment record
-  is kept at the tail of its segment. Segments are added by pushing
-  segment records onto the list headed by &mstate.seg for the
-  containing mstate.
-
-  Segment flags control allocation/merge/deallocation policies:
-  * If EXTERN_BIT set, then we did not allocate this segment,
-    and so should not try to deallocate or merge with others.
-    (This currently holds only for the initial segment passed
-    into create_mspace_with_base.)
-  * If IS_MMAPPED_BIT set, the segment may be merged with
-    other surrounding mmapped segments and trimmed/de-allocated
-    using munmap.
-  * If neither bit is set, then the segment was obtained using
-    MORECORE so can be merged with surrounding MORECORE'd segments
-    and deallocated/trimmed using MORECORE with negative arguments.
-*/
-
-struct malloc_segment {
-  char*        base;             /* base address */
-  size_t       size;             /* allocated size */
-  struct malloc_segment* next;   /* ptr to next segment */
-  flag_t       sflags;           /* mmap and extern flag */
-};
-
-#define is_mmapped_segment(S)  ((S)->sflags & IS_MMAPPED_BIT)
-#define is_extern_segment(S)   ((S)->sflags & EXTERN_BIT)
-
-typedef struct malloc_segment  msegment;
-typedef struct malloc_segment* msegmentptr;
-
-/* ---------------------------- malloc_state ----------------------------- */
-
-/*
-   A malloc_state holds all of the bookkeeping for a space.
-   The main fields are:
-
-  Top
-    The topmost chunk of the currently active segment. Its size is
-    cached in topsize.  The actual size of topmost space is
-    topsize+TOP_FOOT_SIZE, which includes space reserved for adding
-    fenceposts and segment records if necessary when getting more
-    space from the system.  The size at which to autotrim top is
-    cached from mparams in trim_check, except that it is disabled if
-    an autotrim fails.
-
-  Designated victim (dv)
-    This is the preferred chunk for servicing small requests that
-    don't have exact fits.  It is normally the chunk split off most
-    recently to service another small request.  Its size is cached in
-    dvsize. The link fields of this chunk are not maintained since it
-    is not kept in a bin.
-
-  SmallBins
-    An array of bin headers for free chunks.  These bins hold chunks
-    with sizes less than MIN_LARGE_SIZE bytes. Each bin contains
-    chunks of all the same size, spaced 8 bytes apart.  To simplify
-    use in double-linked lists, each bin header acts as a malloc_chunk
-    pointing to the real first node, if it exists (else pointing to
-    itself).  This avoids special-casing for headers.  But to avoid
-    waste, we allocate only the fd/bk pointers of bins, and then use
-    repositioning tricks to treat these as the fields of a chunk.
-
-  TreeBins
-    Treebins are pointers to the roots of trees holding a range of
-    sizes. There are 2 equally spaced treebins for each power of two
-    from TREE_SHIFT to TREE_SHIFT+16. The last bin holds anything
-    larger.
-
-  Bin maps
-    There is one bit map for small bins ("smallmap") and one for
-    treebins ("treemap).  Each bin sets its bit when non-empty, and
-    clears the bit when empty.  Bit operations are then used to avoid
-    bin-by-bin searching -- nearly all "search" is done without ever
-    looking at bins that won't be selected.  The bit maps
-    conservatively use 32 bits per map word, even if on 64bit system.
-    For a good description of some of the bit-based techniques used
-    here, see Henry S. Warren Jr's book "Hacker's Delight" (and
-    supplement at http://hackersdelight.org/). Many of these are
-    intended to reduce the branchiness of paths through malloc etc, as
-    well as to reduce the number of memory locations read or written.
-
-  Segments
-    A list of segments headed by an embedded malloc_segment record
-    representing the initial space.
-
-  Address check support
-    The least_addr field is the least address ever obtained from
-    MORECORE or MMAP. Attempted frees and reallocs of any address less
-    than this are trapped (unless INSECURE is defined).
-
-  Magic tag
-    A cross-check field that should always hold same value as mparams.magic.
-
-  Flags
-    Bits recording whether to use MMAP, locks, or contiguous MORECORE
-
-  Statistics
-    Each space keeps track of current and maximum system memory
-    obtained via MORECORE or MMAP.
-
-  Trim support
-    Fields holding the amount of unused topmost memory that should trigger
-    timing, and a counter to force periodic scanning to release unused
-    non-topmost segments.
-
-  Locking
-    If USE_LOCKS is defined, the "mutex" lock is acquired and released
-    around every public call using this mspace.
-
-  Extension support
-    A void* pointer and a size_t field that can be used to help implement
-    extensions to this malloc.
-*/
-
-/* Bin types, widths and sizes */
-#define NSMALLBINS        (32U)
-#define NTREEBINS         (32U)
-#define SMALLBIN_SHIFT    (3U)
-#define SMALLBIN_WIDTH    (SIZE_T_ONE << SMALLBIN_SHIFT)
-#define TREEBIN_SHIFT     (8U)
-#define MIN_LARGE_SIZE    (SIZE_T_ONE << TREEBIN_SHIFT)
-#define MAX_SMALL_SIZE    (MIN_LARGE_SIZE - SIZE_T_ONE)
-#define MAX_SMALL_REQUEST (MAX_SMALL_SIZE - CHUNK_ALIGN_MASK - CHUNK_OVERHEAD)
-
-struct malloc_state {
-  binmap_t   smallmap;
-  binmap_t   treemap;
-  size_t     dvsize;
-  size_t     topsize;
-  char*      least_addr;
-  mchunkptr  dv;
-  mchunkptr  top;
-  size_t     trim_check;
-  size_t     release_checks;
-  size_t     magic;
-  mchunkptr  smallbins[(NSMALLBINS+1)*2];
-  tbinptr    treebins[NTREEBINS];
-  size_t     footprint;
-  size_t     max_footprint;
-  flag_t     mflags;
-#if USE_LOCKS
-  MLOCK_T    mutex;     /* locate lock among fields that rarely change */
-#endif /* USE_LOCKS */
-  msegment   seg;
-  void*      extp;      /* Unused but available for extensions */
-  size_t     exts;
-};
-
-typedef struct malloc_state*    mstate;
-
-/* ------------- Global malloc_state and malloc_params ------------------- */
-
-/*
-  malloc_params holds global properties, including those that can be
-  dynamically set using mallopt. There is a single instance, mparams,
-  initialized in init_mparams. Note that the non-zeroness of "magic"
-  also serves as an initialization flag.
-*/
-
-struct malloc_params {
-  volatile size_t magic;
-  size_t page_size;
-  size_t granularity;
-  size_t mmap_threshold;
-  size_t trim_threshold;
-  flag_t default_mflags;
-};
-
-static struct malloc_params mparams;
-
-/* Ensure mparams initialized */
-#define ensure_initialization() ((void)(mparams.magic != 0 || init_mparams()))
-
-#if !ONLY_MSPACES
-
-/* The global malloc_state used for all non-"mspace" calls */
-static struct malloc_state _gm_;
-#define gm                 (&_gm_)
-#define is_global(M)       ((M) == &_gm_)
-
-#endif /* !ONLY_MSPACES */
-
-#define is_initialized(M)  ((M)->top != 0)
-
-/* -------------------------- system alloc setup ------------------------- */
-
-/* Operations on mflags */
-
-#define use_lock(M)           ((M)->mflags &   USE_LOCK_BIT)
-#define enable_lock(M)        ((M)->mflags |=  USE_LOCK_BIT)
-#define disable_lock(M)       ((M)->mflags &= ~USE_LOCK_BIT)
-
-#define use_mmap(M)           ((M)->mflags &   USE_MMAP_BIT)
-#define enable_mmap(M)        ((M)->mflags |=  USE_MMAP_BIT)
-#define disable_mmap(M)       ((M)->mflags &= ~USE_MMAP_BIT)
-
-#define use_noncontiguous(M)  ((M)->mflags &   USE_NONCONTIGUOUS_BIT)
-#define disable_contiguous(M) ((M)->mflags |=  USE_NONCONTIGUOUS_BIT)
-
-#define set_lock(M,L)\
- ((M)->mflags = (L)?\
-  ((M)->mflags | USE_LOCK_BIT) :\
-  ((M)->mflags & ~USE_LOCK_BIT))
-
-/* page-align a size */
-#define page_align(S)\
- (((S) + (mparams.page_size - SIZE_T_ONE)) & ~(mparams.page_size - SIZE_T_ONE))
-
-/* granularity-align a size */
-#define granularity_align(S)\
-  (((S) + (mparams.granularity - SIZE_T_ONE))\
-   & ~(mparams.granularity - SIZE_T_ONE))
-
-
-/* For mmap, use granularity alignment on windows, else page-align */
-#ifdef WIN32
-#define mmap_align(S) granularity_align(S)
-#else
-#define mmap_align(S) page_align(S)
-#endif
-
-/* For sys_alloc, enough padding to ensure can malloc request on success */
-#define SYS_ALLOC_PADDING (TOP_FOOT_SIZE + MALLOC_ALIGNMENT)
-
-#define is_page_aligned(S)\
-   (((size_t)(S) & (mparams.page_size - SIZE_T_ONE)) == 0)
-#define is_granularity_aligned(S)\
-   (((size_t)(S) & (mparams.granularity - SIZE_T_ONE)) == 0)
-
-/*  True if segment S holds address A */
-#define segment_holds(S, A)\
-  ((char*)(A) >= S->base && (char*)(A) < S->base + S->size)
-
-/* Return segment holding given address */
-static msegmentptr segment_holding(mstate m, char* addr) {
-  msegmentptr sp = &m->seg;
-  for (;;) {
-    if (addr >= sp->base && addr < sp->base + sp->size)
-      return sp;
-    if ((sp = sp->next) == 0)
-      return 0;
-  }
-}
-
-/* Return true if segment contains a segment link */
-static int has_segment_link(mstate m, msegmentptr ss) {
-  msegmentptr sp = &m->seg;
-  for (;;) {
-    if ((char*)sp >= ss->base && (char*)sp < ss->base + ss->size)
-      return 1;
-    if ((sp = sp->next) == 0)
-      return 0;
-  }
-}
-
-#ifndef MORECORE_CANNOT_TRIM
-#define should_trim(M,s)  ((s) > (M)->trim_check)
-#else  /* MORECORE_CANNOT_TRIM */
-#define should_trim(M,s)  (0)
-#endif /* MORECORE_CANNOT_TRIM */
-
-/*
-  TOP_FOOT_SIZE is padding at the end of a segment, including space
-  that may be needed to place segment records and fenceposts when new
-  noncontiguous segments are added.
-*/
-#define TOP_FOOT_SIZE\
-  (align_offset(chunk2mem(0))+pad_request(sizeof(struct malloc_segment))+MIN_CHUNK_SIZE)
-
-
-/* -------------------------------  Hooks -------------------------------- */
-
-/*
-  PREACTION should be defined to return 0 on success, and nonzero on
-  failure. If you are not using locking, you can redefine these to do
-  anything you like.
-*/
-
-#if USE_LOCKS
-
-#define PREACTION(M)  ((use_lock(M))? ACQUIRE_LOCK(&(M)->mutex) : 0)
-#define POSTACTION(M) { if (use_lock(M)) RELEASE_LOCK(&(M)->mutex); }
-#else /* USE_LOCKS */
-
-#ifndef PREACTION
-#define PREACTION(M) (0)
-#endif  /* PREACTION */
-
-#ifndef POSTACTION
-#define POSTACTION(M)
-#endif  /* POSTACTION */
-
-#endif /* USE_LOCKS */
-
-/*
-  CORRUPTION_ERROR_ACTION is triggered upon detected bad addresses.
-  USAGE_ERROR_ACTION is triggered on detected bad frees and
-  reallocs. The argument p is an address that might have triggered the
-  fault. It is ignored by the two predefined actions, but might be
-  useful in custom actions that try to help diagnose errors.
-*/
-
-#if PROCEED_ON_ERROR
-
-/* A count of the number of corruption errors causing resets */
-int malloc_corruption_error_count;
-
-/* default corruption action */
-static void reset_on_error(mstate m);
-
-#define CORRUPTION_ERROR_ACTION(m)  reset_on_error(m)
-#define USAGE_ERROR_ACTION(m, p)
-
-#else /* PROCEED_ON_ERROR */
-
-#ifndef CORRUPTION_ERROR_ACTION
-#define CORRUPTION_ERROR_ACTION(m) ABORT
-#endif /* CORRUPTION_ERROR_ACTION */
-
-#ifndef USAGE_ERROR_ACTION
-#define USAGE_ERROR_ACTION(m,p) ABORT
-#endif /* USAGE_ERROR_ACTION */
-
-#endif /* PROCEED_ON_ERROR */
-
-/* -------------------------- Debugging setup ---------------------------- */
-
-#if ! DEBUG
-
-#define check_free_chunk(M,P)
-#define check_inuse_chunk(M,P)
-#define check_malloced_chunk(M,P,N)
-#define check_mmapped_chunk(M,P)
-#define check_malloc_state(M)
-#define check_top_chunk(M,P)
-
-#else /* DEBUG */
-#define check_free_chunk(M,P)       do_check_free_chunk(M,P)
-#define check_inuse_chunk(M,P)      do_check_inuse_chunk(M,P)
-#define check_top_chunk(M,P)        do_check_top_chunk(M,P)
-#define check_malloced_chunk(M,P,N) do_check_malloced_chunk(M,P,N)
-#define check_mmapped_chunk(M,P)    do_check_mmapped_chunk(M,P)
-#define check_malloc_state(M)       do_check_malloc_state(M)
-
-static void   do_check_any_chunk(mstate m, mchunkptr p);
-static void   do_check_top_chunk(mstate m, mchunkptr p);
-static void   do_check_mmapped_chunk(mstate m, mchunkptr p);
-static void   do_check_inuse_chunk(mstate m, mchunkptr p);
-static void   do_check_free_chunk(mstate m, mchunkptr p);
-static void   do_check_malloced_chunk(mstate m, void* mem, size_t s);
-static void   do_check_tree(mstate m, tchunkptr t);
-static void   do_check_treebin(mstate m, bindex_t i);
-static void   do_check_smallbin(mstate m, bindex_t i);
-static void   do_check_malloc_state(mstate m);
-static int    bin_find(mstate m, mchunkptr x);
-static size_t traverse_and_check(mstate m);
-#endif /* DEBUG */
-
-/* ---------------------------- Indexing Bins ---------------------------- */
-
-#define is_small(s)         (((s) >> SMALLBIN_SHIFT) < NSMALLBINS)
-#define small_index(s)      ((s)  >> SMALLBIN_SHIFT)
-#define small_index2size(i) ((i)  << SMALLBIN_SHIFT)
-#define MIN_SMALL_INDEX     (small_index(MIN_CHUNK_SIZE))
-
-/* addressing by index. See above about smallbin repositioning */
-#define smallbin_at(M, i)   ((sbinptr)((char*)&((M)->smallbins[(i)<<1])))
-#define treebin_at(M,i)     (&((M)->treebins[i]))
-
-/* assign tree index for size S to variable I. Use x86 asm if possible  */
-#if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))
-#define compute_tree_index(S, I)\
-{\
-  unsigned int X = S >> TREEBIN_SHIFT;\
-  if (X == 0)\
-    I = 0;\
-  else if (X > 0xFFFF)\
-    I = NTREEBINS-1;\
-  else {\
-    unsigned int K;\
-    __asm__("bsrl\t%1, %0\n\t" : "=r" (K) : "rm"  (X));\
-    I =  (bindex_t)((K << 1) + ((S >> (K + (TREEBIN_SHIFT-1)) & 1)));\
-  }\
-}
-
-#elif defined (__INTEL_COMPILER)
-#define compute_tree_index(S, I)\
-{\
-  size_t X = S >> TREEBIN_SHIFT;\
-  if (X == 0)\
-    I = 0;\
-  else if (X > 0xFFFF)\
-    I = NTREEBINS-1;\
-  else {\
-    unsigned int K = _bit_scan_reverse (X); \
-    I =  (bindex_t)((K << 1) + ((S >> (K + (TREEBIN_SHIFT-1)) & 1)));\
-  }\
-}
-
-#elif defined(_MSC_VER) && _MSC_VER>=1300
-#define compute_tree_index(S, I)\
-{\
-  size_t X = S >> TREEBIN_SHIFT;\
-  if (X == 0)\
-    I = 0;\
-  else if (X > 0xFFFF)\
-    I = NTREEBINS-1;\
-  else {\
-    unsigned int K;\
-    _BitScanReverse((DWORD *) &K, X);\
-    I =  (bindex_t)((K << 1) + ((S >> (K + (TREEBIN_SHIFT-1)) & 1)));\
-  }\
-}
-
-#else /* GNUC */
-#define compute_tree_index(S, I)\
-{\
-  size_t X = S >> TREEBIN_SHIFT;\
-  if (X == 0)\
-    I = 0;\
-  else if (X > 0xFFFF)\
-    I = NTREEBINS-1;\
-  else {\
-    unsigned int Y = (unsigned int)X;\
-    unsigned int N = ((Y - 0x100) >> 16) & 8;\
-    unsigned int K = (((Y <<= N) - 0x1000) >> 16) & 4;\
-    N += K;\
-    N += K = (((Y <<= K) - 0x4000) >> 16) & 2;\
-    K = 14 - N + ((Y <<= K) >> 15);\
-    I = (K << 1) + ((S >> (K + (TREEBIN_SHIFT-1)) & 1));\
-  }\
-}
-#endif /* GNUC */
-
-/* Bit representing maximum resolved size in a treebin at i */
-#define bit_for_tree_index(i) \
-   (i == NTREEBINS-1)? (SIZE_T_BITSIZE-1) : (((i) >> 1) + TREEBIN_SHIFT - 2)
-
-/* Shift placing maximum resolved bit in a treebin at i as sign bit */
-#define leftshift_for_tree_index(i) \
-   ((i == NTREEBINS-1)? 0 : \
-    ((SIZE_T_BITSIZE-SIZE_T_ONE) - (((i) >> 1) + TREEBIN_SHIFT - 2)))
-
-/* The size of the smallest chunk held in bin with index i */
-#define minsize_for_tree_index(i) \
-   ((SIZE_T_ONE << (((i) >> 1) + TREEBIN_SHIFT)) |  \
-   (((size_t)((i) & SIZE_T_ONE)) << (((i) >> 1) + TREEBIN_SHIFT - 1)))
-
-
-/* ------------------------ Operations on bin maps ----------------------- */
-
-/* bit corresponding to given index */
-#define idx2bit(i)              ((binmap_t)(1) << (i))
-
-/* Mark/Clear bits with given index */
-#define mark_smallmap(M,i)      ((M)->smallmap |=  idx2bit(i))
-#define clear_smallmap(M,i)     ((M)->smallmap &= ~idx2bit(i))
-#define smallmap_is_marked(M,i) ((M)->smallmap &   idx2bit(i))
-
-#define mark_treemap(M,i)       ((M)->treemap  |=  idx2bit(i))
-#define clear_treemap(M,i)      ((M)->treemap  &= ~idx2bit(i))
-#define treemap_is_marked(M,i)  ((M)->treemap  &   idx2bit(i))
-
-/* isolate the least set bit of a bitmap */
-#define least_bit(x)         ((x) & -(x))
-
-/* mask with all bits to left of least bit of x on */
-#define left_bits(x)         ((x<<1) | -(x<<1))
-
-/* mask with all bits to left of or equal to least bit of x on */
-#define same_or_left_bits(x) ((x) | -(x))
-
-/* index corresponding to given bit. Use x86 asm if possible */
-
-#if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))
-#define compute_bit2idx(X, I)\
-{\
-  unsigned int J;\
-  __asm__("bsfl\t%1, %0\n\t" : "=r" (J) : "rm" (X));\
-  I = (bindex_t)J;\
-}
-
-#elif defined (__INTEL_COMPILER)
-#define compute_bit2idx(X, I)\
-{\
-  unsigned int J;\
-  J = _bit_scan_forward (X); \
-  I = (bindex_t)J;\
-}
-
-#elif defined(_MSC_VER) && _MSC_VER>=1300
-#define compute_bit2idx(X, I)\
-{\
-  unsigned int J;\
-  _BitScanForward((DWORD *) &J, X);\
-  I = (bindex_t)J;\
-}
-
-#elif USE_BUILTIN_FFS
-#define compute_bit2idx(X, I) I = ffs(X)-1
-
-#else
-#define compute_bit2idx(X, I)\
-{\
-  unsigned int Y = X - 1;\
-  unsigned int K = Y >> (16-4) & 16;\
-  unsigned int N = K;        Y >>= K;\
-  N += K = Y >> (8-3) &  8;  Y >>= K;\
-  N += K = Y >> (4-2) &  4;  Y >>= K;\
-  N += K = Y >> (2-1) &  2;  Y >>= K;\
-  N += K = Y >> (1-0) &  1;  Y >>= K;\
-  I = (bindex_t)(N + Y);\
-}
-#endif /* GNUC */
-
-
-/* ----------------------- Runtime Check Support ------------------------- */
-
-/*
-  For security, the main invariant is that malloc/free/etc never
-  writes to a static address other than malloc_state, unless static
-  malloc_state itself has been corrupted, which cannot occur via
-  malloc (because of these checks). In essence this means that we
-  believe all pointers, sizes, maps etc held in malloc_state, but
-  check all of those linked or offsetted from other embedded data
-  structures.  These checks are interspersed with main code in a way
-  that tends to minimize their run-time cost.
-
-  When FOOTERS is defined, in addition to range checking, we also
-  verify footer fields of inuse chunks, which can be used guarantee
-  that the mstate controlling malloc/free is intact.  This is a
-  streamlined version of the approach described by William Robertson
-  et al in "Run-time Detection of Heap-based Overflows" LISA'03
-  http://www.usenix.org/events/lisa03/tech/robertson.html The footer
-  of an inuse chunk holds the xor of its mstate and a random seed,
-  that is checked upon calls to free() and realloc().  This is
-  (probablistically) unguessable from outside the program, but can be
-  computed by any code successfully malloc'ing any chunk, so does not
-  itself provide protection against code that has already broken
-  security through some other means.  Unlike Robertson et al, we
-  always dynamically check addresses of all offset chunks (previous,
-  next, etc). This turns out to be cheaper than relying on hashes.
-*/
-
-#if !INSECURE
-/* Check if address a is at least as high as any from MORECORE or MMAP */
-#define ok_address(M, a) ((char*)(a) >= (M)->least_addr)
-/* Check if address of next chunk n is higher than base chunk p */
-#define ok_next(p, n)    ((char*)(p) < (char*)(n))
-/* Check if p has its cinuse bit on */
-#define ok_cinuse(p)     cinuse(p)
-/* Check if p has its pinuse bit on */
-#define ok_pinuse(p)     pinuse(p)
-
-#else /* !INSECURE */
-#define ok_address(M, a) (1)
-#define ok_next(b, n)    (1)
-#define ok_cinuse(p)     (1)
-#define ok_pinuse(p)     (1)
-#endif /* !INSECURE */
-
-#if (FOOTERS && !INSECURE)
-/* Check if (alleged) mstate m has expected magic field */
-#define ok_magic(M)      ((M)->magic == mparams.magic)
-#else  /* (FOOTERS && !INSECURE) */
-#define ok_magic(M)      (1)
-#endif /* (FOOTERS && !INSECURE) */
-
-
-/* In gcc, use __builtin_expect to minimize impact of checks */
-#if !INSECURE
-#if defined(__GNUC__) && __GNUC__ >= 3
-#define RTCHECK(e)  __builtin_expect(e, 1)
-#else /* GNUC */
-#define RTCHECK(e)  (e)
-#endif /* GNUC */
-#else /* !INSECURE */
-#define RTCHECK(e)  (1)
-#endif /* !INSECURE */
-
-/* macros to set up inuse chunks with or without footers */
-
-#if !FOOTERS
-
-#define mark_inuse_foot(M,p,s)
-
-/* Set cinuse bit and pinuse bit of next chunk */
-#define set_inuse(M,p,s)\
-  ((p)->head = (((p)->head & PINUSE_BIT)|s|CINUSE_BIT),\
-  ((mchunkptr)(((char*)(p)) + (s)))->head |= PINUSE_BIT)
-
-/* Set cinuse and pinuse of this chunk and pinuse of next chunk */
-#define set_inuse_and_pinuse(M,p,s)\
-  ((p)->head = (s|PINUSE_BIT|CINUSE_BIT),\
-  ((mchunkptr)(((char*)(p)) + (s)))->head |= PINUSE_BIT)
-
-/* Set size, cinuse and pinuse bit of this chunk */
-#define set_size_and_pinuse_of_inuse_chunk(M, p, s)\
-  ((p)->head = (s|PINUSE_BIT|CINUSE_BIT))
-
-#else /* FOOTERS */
-
-/* Set foot of inuse chunk to be xor of mstate and seed */
-#define mark_inuse_foot(M,p,s)\
-  (((mchunkptr)((char*)(p) + (s)))->prev_foot = ((size_t)(M) ^ mparams.magic))
-
-#define get_mstate_for(p)\
-  ((mstate)(((mchunkptr)((char*)(p) +\
-    (chunksize(p))))->prev_foot ^ mparams.magic))
-
-#define set_inuse(M,p,s)\
-  ((p)->head = (((p)->head & PINUSE_BIT)|s|CINUSE_BIT),\
-  (((mchunkptr)(((char*)(p)) + (s)))->head |= PINUSE_BIT), \
-  mark_inuse_foot(M,p,s))
-
-#define set_inuse_and_pinuse(M,p,s)\
-  ((p)->head = (s|PINUSE_BIT|CINUSE_BIT),\
-  (((mchunkptr)(((char*)(p)) + (s)))->head |= PINUSE_BIT),\
- mark_inuse_foot(M,p,s))
-
-#define set_size_and_pinuse_of_inuse_chunk(M, p, s)\
-  ((p)->head = (s|PINUSE_BIT|CINUSE_BIT),\
-  mark_inuse_foot(M, p, s))
-
-#endif /* !FOOTERS */
-
-/* ---------------------------- setting mparams -------------------------- */
-
-/* Initialize mparams */
-static int init_mparams(void) {
-#ifdef NEED_GLOBAL_LOCK_INIT
-  if (malloc_global_mutex_status <= 0)
-    init_malloc_global_mutex();
-#endif
-
-  ACQUIRE_MALLOC_GLOBAL_LOCK();
-  if (mparams.magic == 0) {
-    size_t magic;
-    size_t psize;
-    size_t gsize;
-
-#ifndef WIN32
-    psize = malloc_getpagesize;
-    gsize = ((DEFAULT_GRANULARITY != 0)? DEFAULT_GRANULARITY : psize);
-#else /* WIN32 */
-    {
-      SYSTEM_INFO system_info;
-      GetSystemInfo(&system_info);
-      psize = system_info.dwPageSize;
-      gsize = ((DEFAULT_GRANULARITY != 0)?
-	       DEFAULT_GRANULARITY : system_info.dwAllocationGranularity);
-    }
-#endif /* WIN32 */
-
-    /* Sanity-check configuration:
-       size_t must be unsigned and as wide as pointer type.
-       ints must be at least 4 bytes.
-       alignment must be at least 8.
-       Alignment, min chunk size, and page size must all be powers of 2.
-    */
-    if ((sizeof(size_t) != sizeof(char*)) ||
-	(MAX_SIZE_T < MIN_CHUNK_SIZE)  ||
-	(sizeof(int) < 4)  ||
-	(MALLOC_ALIGNMENT < (size_t)8U) ||
-	((MALLOC_ALIGNMENT & (MALLOC_ALIGNMENT-SIZE_T_ONE)) != 0) ||
-	((MCHUNK_SIZE      & (MCHUNK_SIZE-SIZE_T_ONE))      != 0) ||
-	((gsize            & (gsize-SIZE_T_ONE))            != 0) ||
-	((psize            & (psize-SIZE_T_ONE))            != 0))
-      ABORT;
-
-    mparams.granularity = gsize;
-    mparams.page_size = psize;
-    mparams.mmap_threshold = DEFAULT_MMAP_THRESHOLD;
-    mparams.trim_threshold = DEFAULT_TRIM_THRESHOLD;
-#if MORECORE_CONTIGUOUS
-    mparams.default_mflags = USE_LOCK_BIT|USE_MMAP_BIT;
-#else  /* MORECORE_CONTIGUOUS */
-    mparams.default_mflags = USE_LOCK_BIT|USE_MMAP_BIT|USE_NONCONTIGUOUS_BIT;
-#endif /* MORECORE_CONTIGUOUS */
-
-#if !ONLY_MSPACES
-    /* Set up lock for main malloc area */
-    gm->mflags = mparams.default_mflags;
-    (void)INITIAL_LOCK(&gm->mutex);
-#endif
-
-#if (FOOTERS && !INSECURE)
-    {
-#if USE_DEV_RANDOM
-      int fd;
-      unsigned char buf[sizeof(size_t)];
-      /* Try to use /dev/urandom, else fall back on using time */
-      if ((fd = open("/dev/urandom", O_RDONLY)) >= 0 &&
-	  read(fd, buf, sizeof(buf)) == sizeof(buf)) {
-	magic = *((size_t *) buf);
-	close(fd);
-      }
-      else
-#endif /* USE_DEV_RANDOM */
-#ifdef WIN32
-	magic = (size_t)(GetTickCount() ^ (size_t)0x55555555U);
-#else
-      magic = (size_t)(time(0) ^ (size_t)0x55555555U);
-#endif
-      magic |= (size_t)8U;    /* ensure nonzero */
-      magic &= ~(size_t)7U;   /* improve chances of fault for bad values */
-    }
-#else /* (FOOTERS && !INSECURE) */
-    magic = (size_t)0x58585858U;
-#endif /* (FOOTERS && !INSECURE) */
-
-    mparams.magic = magic;
-  }
-
-  RELEASE_MALLOC_GLOBAL_LOCK();
-  return 1;
-}
-
-/* support for mallopt */
-static int change_mparam(int param_number, int value) {
-  size_t val = (value == -1)? MAX_SIZE_T : (size_t)value;
-  ensure_initialization();
-  switch(param_number) {
-  case M_TRIM_THRESHOLD:
-    mparams.trim_threshold = val;
-    return 1;
-  case M_GRANULARITY:
-    if (val >= mparams.page_size && ((val & (val-1)) == 0)) {
-      mparams.granularity = val;
-      return 1;
-    }
-    else
-      return 0;
-  case M_MMAP_THRESHOLD:
-    mparams.mmap_threshold = val;
-    return 1;
-  default:
-    return 0;
-  }
-}
-
-#if DEBUG
-/* ------------------------- Debugging Support --------------------------- */
-
-/* Check properties of any chunk, whether free, inuse, mmapped etc  */
-static void do_check_any_chunk(mstate m, mchunkptr p) {
-  assert((is_aligned(chunk2mem(p))) || (p->head == FENCEPOST_HEAD));
-  assert(ok_address(m, p));
-}
-
-/* Check properties of top chunk */
-static void do_check_top_chunk(mstate m, mchunkptr p) {
-  msegmentptr sp = segment_holding(m, (char*)p);
-  size_t  sz = p->head & ~INUSE_BITS; /* third-lowest bit can be set! */
-  assert(sp != 0);
-  assert((is_aligned(chunk2mem(p))) || (p->head == FENCEPOST_HEAD));
-  assert(ok_address(m, p));
-  assert(sz == m->topsize);
-  assert(sz > 0);
-  assert(sz == ((sp->base + sp->size) - (char*)p) - TOP_FOOT_SIZE);
-  assert(pinuse(p));
-  assert(!pinuse(chunk_plus_offset(p, sz)));
-}
-
-/* Check properties of (inuse) mmapped chunks */
-static void do_check_mmapped_chunk(mstate m, mchunkptr p) {
-  size_t  sz = chunksize(p);
-  size_t len = (sz + (p->prev_foot & ~IS_MMAPPED_BIT) + MMAP_FOOT_PAD);
-  assert(is_mmapped(p));
-  assert(use_mmap(m));
-  assert((is_aligned(chunk2mem(p))) || (p->head == FENCEPOST_HEAD));
-  assert(ok_address(m, p));
-  assert(!is_small(sz));
-  assert((len & (mparams.page_size-SIZE_T_ONE)) == 0);
-  assert(chunk_plus_offset(p, sz)->head == FENCEPOST_HEAD);
-  assert(chunk_plus_offset(p, sz+SIZE_T_SIZE)->head == 0);
-}
-
-/* Check properties of inuse chunks */
-static void do_check_inuse_chunk(mstate m, mchunkptr p) {
-  do_check_any_chunk(m, p);
-  assert(cinuse(p));
-  assert(next_pinuse(p));
-  /* If not pinuse and not mmapped, previous chunk has OK offset */
-  assert(is_mmapped(p) || pinuse(p) || next_chunk(prev_chunk(p)) == p);
-  if (is_mmapped(p))
-    do_check_mmapped_chunk(m, p);
-}
-
-/* Check properties of free chunks */
-static void do_check_free_chunk(mstate m, mchunkptr p) {
-  size_t sz = chunksize(p);
-  mchunkptr next = chunk_plus_offset(p, sz);
-  do_check_any_chunk(m, p);
-  assert(!cinuse(p));
-  assert(!next_pinuse(p));
-  assert (!is_mmapped(p));
-  if (p != m->dv && p != m->top) {
-    if (sz >= MIN_CHUNK_SIZE) {
-      assert((sz & CHUNK_ALIGN_MASK) == 0);
-      assert(is_aligned(chunk2mem(p)));
-      assert(next->prev_foot == sz);
-      assert(pinuse(p));
-      assert (next == m->top || cinuse(next));
-      assert(p->fd->bk == p);
-      assert(p->bk->fd == p);
-    }
-    else  /* markers are always of size SIZE_T_SIZE */
-      assert(sz == SIZE_T_SIZE);
-  }
-}
-
-/* Check properties of malloced chunks at the point they are malloced */
-static void do_check_malloced_chunk(mstate m, void* mem, size_t s) {
-  if (mem != 0) {
-    mchunkptr p = mem2chunk(mem);
-    size_t sz = p->head & ~(PINUSE_BIT|CINUSE_BIT);
-    do_check_inuse_chunk(m, p);
-    assert((sz & CHUNK_ALIGN_MASK) == 0);
-    assert(sz >= MIN_CHUNK_SIZE);
-    assert(sz >= s);
-    /* unless mmapped, size is less than MIN_CHUNK_SIZE more than request */
-    assert(is_mmapped(p) || sz < (s + MIN_CHUNK_SIZE));
-  }
-}
-
-/* Check a tree and its subtrees.  */
-static void do_check_tree(mstate m, tchunkptr t) {
-  tchunkptr head = 0;
-  tchunkptr u = t;
-  bindex_t tindex = t->index;
-  size_t tsize = chunksize(t);
-  bindex_t idx;
-  compute_tree_index(tsize, idx);
-  assert(tindex == idx);
-  assert(tsize >= MIN_LARGE_SIZE);
-  assert(tsize >= minsize_for_tree_index(idx));
-  assert((idx == NTREEBINS-1) || (tsize < minsize_for_tree_index((idx+1))));
-
-  do { /* traverse through chain of same-sized nodes */
-    do_check_any_chunk(m, ((mchunkptr)u));
-    assert(u->index == tindex);
-    assert(chunksize(u) == tsize);
-    assert(!cinuse(u));
-    assert(!next_pinuse(u));
-    assert(u->fd->bk == u);
-    assert(u->bk->fd == u);
-    if (u->parent == 0) {
-      assert(u->child[0] == 0);
-      assert(u->child[1] == 0);
-    }
-    else {
-      assert(head == 0); /* only one node on chain has parent */
-      head = u;
-      assert(u->parent != u);
-      assert (u->parent->child[0] == u ||
-	      u->parent->child[1] == u ||
-	      *((tbinptr*)(u->parent)) == u);
-      if (u->child[0] != 0) {
-	assert(u->child[0]->parent == u);
-	assert(u->child[0] != u);
-	do_check_tree(m, u->child[0]);
-      }
-      if (u->child[1] != 0) {
-	assert(u->child[1]->parent == u);
-	assert(u->child[1] != u);
-	do_check_tree(m, u->child[1]);
-      }
-      if (u->child[0] != 0 && u->child[1] != 0) {
-	assert(chunksize(u->child[0]) < chunksize(u->child[1]));
-      }
-    }
-    u = u->fd;
-  } while (u != t);
-  assert(head != 0);
-}
-
-/*  Check all the chunks in a treebin.  */
-static void do_check_treebin(mstate m, bindex_t i) {
-  tbinptr* tb = treebin_at(m, i);
-  tchunkptr t = *tb;
-  int empty = (m->treemap & (1U << i)) == 0;
-  if (t == 0)
-    assert(empty);
-  if (!empty)
-    do_check_tree(m, t);
-}
-
-/*  Check all the chunks in a smallbin.  */
-static void do_check_smallbin(mstate m, bindex_t i) {
-  sbinptr b = smallbin_at(m, i);
-  mchunkptr p = b->bk;
-  unsigned int empty = (m->smallmap & (1U << i)) == 0;
-  if (p == b)
-    assert(empty);
-  if (!empty) {
-    for (; p != b; p = p->bk) {
-      size_t size = chunksize(p);
-      mchunkptr q;
-      /* each chunk claims to be free */
-      do_check_free_chunk(m, p);
-      /* chunk belongs in bin */
-      assert(small_index(size) == i);
-      assert(p->bk == b || chunksize(p->bk) == chunksize(p));
-      /* chunk is followed by an inuse chunk */
-      q = next_chunk(p);
-      if (q->head != FENCEPOST_HEAD)
-	do_check_inuse_chunk(m, q);
-    }
-  }
-}
-
-/* Find x in a bin. Used in other check functions. */
-static int bin_find(mstate m, mchunkptr x) {
-  size_t size = chunksize(x);
-  if (is_small(size)) {
-    bindex_t sidx = small_index(size);
-    sbinptr b = smallbin_at(m, sidx);
-    if (smallmap_is_marked(m, sidx)) {
-      mchunkptr p = b;
-      do {
-	if (p == x)
-	  return 1;
-      } while ((p = p->fd) != b);
-    }
-  }
-  else {
-    bindex_t tidx;
-    compute_tree_index(size, tidx);
-    if (treemap_is_marked(m, tidx)) {
-      tchunkptr t = *treebin_at(m, tidx);
-      size_t sizebits = size << leftshift_for_tree_index(tidx);
-      while (t != 0 && chunksize(t) != size) {
-	t = t->child[(sizebits >> (SIZE_T_BITSIZE-SIZE_T_ONE)) & 1];
-	sizebits <<= 1;
-      }
-      if (t != 0) {
-	tchunkptr u = t;
-	do {
-	  if (u == (tchunkptr)x)
-	    return 1;
-	} while ((u = u->fd) != t);
-      }
-    }
-  }
-  return 0;
-}
-
-/* Traverse each chunk and check it; return total */
-static size_t traverse_and_check(mstate m) {
-  size_t sum = 0;
-  if (is_initialized(m)) {
-    msegmentptr s = &m->seg;
-    sum += m->topsize + TOP_FOOT_SIZE;
-    while (s != 0) {
-      mchunkptr q = align_as_chunk(s->base);
-      mchunkptr lastq = 0;
-      assert(pinuse(q));
-      while (segment_holds(s, q) &&
-	     q != m->top && q->head != FENCEPOST_HEAD) {
-	sum += chunksize(q);
-	if (cinuse(q)) {
-	  assert(!bin_find(m, q));
-	  do_check_inuse_chunk(m, q);
-	}
-	else {
-	  assert(q == m->dv || bin_find(m, q));
-	  assert(lastq == 0 || cinuse(lastq)); /* Not 2 consecutive free */
-	  do_check_free_chunk(m, q);
-	}
-	lastq = q;
-	q = next_chunk(q);
-      }
-      s = s->next;
-    }
-  }
-  return sum;
-}
-
-/* Check all properties of malloc_state. */
-static void do_check_malloc_state(mstate m) {
-  bindex_t i;
-  size_t total;
-  /* check bins */
-  for (i = 0; i < NSMALLBINS; ++i)
-    do_check_smallbin(m, i);
-  for (i = 0; i < NTREEBINS; ++i)
-    do_check_treebin(m, i);
-
-  if (m->dvsize != 0) { /* check dv chunk */
-    do_check_any_chunk(m, m->dv);
-    assert(m->dvsize == chunksize(m->dv));
-    assert(m->dvsize >= MIN_CHUNK_SIZE);
-    assert(bin_find(m, m->dv) == 0);
-  }
-
-  if (m->top != 0) {   /* check top chunk */
-    do_check_top_chunk(m, m->top);
-    /*assert(m->topsize == chunksize(m->top)); redundant */
-    assert(m->topsize > 0);
-    assert(bin_find(m, m->top) == 0);
-  }
-
-  total = traverse_and_check(m);
-  assert(total <= m->footprint);
-  assert(m->footprint <= m->max_footprint);
-}
-#endif /* DEBUG */
-
-/* ----------------------------- statistics ------------------------------ */
-
-#if !NO_MALLINFO
-static struct mallinfo internal_mallinfo(mstate m) {
-  struct mallinfo nm = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
-  ensure_initialization();
-  if (!PREACTION(m)) {
-    check_malloc_state(m);
-    if (is_initialized(m)) {
-      size_t nfree = SIZE_T_ONE; /* top always free */
-      size_t mfree = m->topsize + TOP_FOOT_SIZE;
-      size_t sum = mfree;
-      msegmentptr s = &m->seg;
-      while (s != 0) {
-	mchunkptr q = align_as_chunk(s->base);
-	while (segment_holds(s, q) &&
-	       q != m->top && q->head != FENCEPOST_HEAD) {
-	  size_t sz = chunksize(q);
-	  sum += sz;
-	  if (!cinuse(q)) {
-	    mfree += sz;
-	    ++nfree;
-	  }
-	  q = next_chunk(q);
-	}
-	s = s->next;
-      }
-
-      nm.arena    = sum;
-      nm.ordblks  = nfree;
-      nm.hblkhd   = m->footprint - sum;
-      nm.usmblks  = m->max_footprint;
-      nm.uordblks = m->footprint - mfree;
-      nm.fordblks = mfree;
-      nm.keepcost = m->topsize;
-    }
-
-    POSTACTION(m);
-  }
-  return nm;
-}
-#endif /* !NO_MALLINFO */
-
-static void internal_malloc_stats(mstate m) {
-  ensure_initialization();
-  if (!PREACTION(m)) {
-    size_t maxfp = 0;
-    size_t fp = 0;
-    size_t used = 0;
-    check_malloc_state(m);
-    if (is_initialized(m)) {
-      msegmentptr s = &m->seg;
-      maxfp = m->max_footprint;
-      fp = m->footprint;
-      used = fp - (m->topsize + TOP_FOOT_SIZE);
-
-      while (s != 0) {
-	mchunkptr q = align_as_chunk(s->base);
-	while (segment_holds(s, q) &&
-	       q != m->top && q->head != FENCEPOST_HEAD) {
-	  if (!cinuse(q))
-	    used -= chunksize(q);
-	  q = next_chunk(q);
-	}
-	s = s->next;
-      }
-    }
-
-    fprintf(stderr, "max system bytes = %10lu\n", (unsigned long)(maxfp));
-    fprintf(stderr, "system bytes     = %10lu\n", (unsigned long)(fp));
-    fprintf(stderr, "in use bytes     = %10lu\n", (unsigned long)(used));
-
-    POSTACTION(m);
-  }
-}
-
-/* ----------------------- Operations on smallbins ----------------------- */
-
-/*
-  Various forms of linking and unlinking are defined as macros.  Even
-  the ones for trees, which are very long but have very short typical
-  paths.  This is ugly but reduces reliance on inlining support of
-  compilers.
-*/
-
-/* Link a free chunk into a smallbin  */
-#define insert_small_chunk(M, P, S) {\
-  bindex_t I  = small_index(S);\
-  mchunkptr B = smallbin_at(M, I);\
-  mchunkptr F = B;\
-  assert(S >= MIN_CHUNK_SIZE);\
-  if (!smallmap_is_marked(M, I))\
-    mark_smallmap(M, I);\
-  else if (RTCHECK(ok_address(M, B->fd)))\
-    F = B->fd;\
-  else {\
-    CORRUPTION_ERROR_ACTION(M);\
-  }\
-  B->fd = P;\
-  F->bk = P;\
-  P->fd = F;\
-  P->bk = B;\
-}
-
-/* Unlink a chunk from a smallbin  */
-#define unlink_small_chunk(M, P, S) {\
-  mchunkptr F = P->fd;\
-  mchunkptr B = P->bk;\
-  bindex_t I = small_index(S);\
-  assert(P != B);\
-  assert(P != F);\
-  assert(chunksize(P) == small_index2size(I));\
-  if (F == B)\
-    clear_smallmap(M, I);\
-  else if (RTCHECK((F == smallbin_at(M,I) || ok_address(M, F)) &&\
-		   (B == smallbin_at(M,I) || ok_address(M, B)))) {\
-    F->bk = B;\
-    B->fd = F;\
-  }\
-  else {\
-    CORRUPTION_ERROR_ACTION(M);\
-  }\
-}
-
-/* Unlink the first chunk from a smallbin */
-#define unlink_first_small_chunk(M, B, P, I) {\
-  mchunkptr F = P->fd;\
-  assert(P != B);\
-  assert(P != F);\
-  assert(chunksize(P) == small_index2size(I));\
-  if (B == F)\
-    clear_smallmap(M, I);\
-  else if (RTCHECK(ok_address(M, F))) {\
-    B->fd = F;\
-    F->bk = B;\
-  }\
-  else {\
-    CORRUPTION_ERROR_ACTION(M);\
-  }\
-}
-
-
-
-/* Replace dv node, binning the old one */
-/* Used only when dvsize known to be small */
-#define replace_dv(M, P, S) {\
-  size_t DVS = M->dvsize;\
-  if (DVS != 0) {\
-    mchunkptr DV = M->dv;\
-    assert(is_small(DVS));\
-    insert_small_chunk(M, DV, DVS);\
-  }\
-  M->dvsize = S;\
-  M->dv = P;\
-}
-
-/* ------------------------- Operations on trees ------------------------- */
-
-/* Insert chunk into tree */
-#define insert_large_chunk(M, X, S) {\
-  tbinptr* H;\
-  bindex_t I;\
-  compute_tree_index(S, I);\
-  H = treebin_at(M, I);\
-  X->index = I;\
-  X->child[0] = X->child[1] = 0;\
-  if (!treemap_is_marked(M, I)) {\
-    mark_treemap(M, I);\
-    *H = X;\
-    X->parent = (tchunkptr)H;\
-    X->fd = X->bk = X;\
-  }\
-  else {\
-    tchunkptr T = *H;\
-    size_t K = S << leftshift_for_tree_index(I);\
-    for (;;) {\
-      if (chunksize(T) != S) {\
-	tchunkptr* C = &(T->child[(K >> (SIZE_T_BITSIZE-SIZE_T_ONE)) & 1]);\
-	K <<= 1;\
-	if (*C != 0)\
-	  T = *C;\
-	else if (RTCHECK(ok_address(M, C))) {\
-	  *C = X;\
-	  X->parent = T;\
-	  X->fd = X->bk = X;\
-	  break;\
-	}\
-	else {\
-	  CORRUPTION_ERROR_ACTION(M);\
-	  break;\
-	}\
-      }\
-      else {\
-	tchunkptr F = T->fd;\
-	if (RTCHECK(ok_address(M, T) && ok_address(M, F))) {\
-	  T->fd = F->bk = X;\
-	  X->fd = F;\
-	  X->bk = T;\
-	  X->parent = 0;\
-	  break;\
-	}\
-	else {\
-	  CORRUPTION_ERROR_ACTION(M);\
-	  break;\
-	}\
-      }\
-    }\
-  }\
-}
-
-/*
-  Unlink steps:
-
-  1. If x is a chained node, unlink it from its same-sized fd/bk links
-     and choose its bk node as its replacement.
-  2. If x was the last node of its size, but not a leaf node, it must
-     be replaced with a leaf node (not merely one with an open left or
-     right), to make sure that lefts and rights of descendants
-     correspond properly to bit masks.  We use the rightmost descendant
-     of x.  We could use any other leaf, but this is easy to locate and
-     tends to counteract removal of leftmosts elsewhere, and so keeps
-     paths shorter than minimally guaranteed.  This doesn't loop much
-     because on average a node in a tree is near the bottom.
-  3. If x is the base of a chain (i.e., has parent links) relink
-     x's parent and children to x's replacement (or null if none).
-*/
-
-#define unlink_large_chunk(M, X) {\
-  tchunkptr XP = X->parent;\
-  tchunkptr R;\
-  if (X->bk != X) {\
-    tchunkptr F = X->fd;\
-    R = X->bk;\
-    if (RTCHECK(ok_address(M, F))) {\
-      F->bk = R;\
-      R->fd = F;\
-    }\
-    else {\
-      CORRUPTION_ERROR_ACTION(M);\
-    }\
-  }\
-  else {\
-    tchunkptr* RP;\
-    if (((R = *(RP = &(X->child[1]))) != 0) ||\
-	((R = *(RP = &(X->child[0]))) != 0)) {\
-      tchunkptr* CP;\
-      while ((*(CP = &(R->child[1])) != 0) ||\
-	     (*(CP = &(R->child[0])) != 0)) {\
-	R = *(RP = CP);\
-      }\
-      if (RTCHECK(ok_address(M, RP)))\
-	*RP = 0;\
-      else {\
-	CORRUPTION_ERROR_ACTION(M);\
-      }\
-    }\
-  }\
-  if (XP != 0) {\
-    tbinptr* H = treebin_at(M, X->index);\
-    if (X == *H) {\
-      if ((*H = R) == 0) \
-	clear_treemap(M, X->index);\
-    }\
-    else if (RTCHECK(ok_address(M, XP))) {\
-      if (XP->child[0] == X) \
-	XP->child[0] = R;\
-      else \
-	XP->child[1] = R;\
-    }\
-    else\
-      CORRUPTION_ERROR_ACTION(M);\
-    if (R != 0) {\
-      if (RTCHECK(ok_address(M, R))) {\
-	tchunkptr C0, C1;\
-	R->parent = XP;\
-	if ((C0 = X->child[0]) != 0) {\
-	  if (RTCHECK(ok_address(M, C0))) {\
-	    R->child[0] = C0;\
-	    C0->parent = R;\
-	  }\
-	  else\
-	    CORRUPTION_ERROR_ACTION(M);\
-	}\
-	if ((C1 = X->child[1]) != 0) {\
-	  if (RTCHECK(ok_address(M, C1))) {\
-	    R->child[1] = C1;\
-	    C1->parent = R;\
-	  }\
-	  else\
-	    CORRUPTION_ERROR_ACTION(M);\
-	}\
-      }\
-      else\
-	CORRUPTION_ERROR_ACTION(M);\
-    }\
-  }\
-}
-
-/* Relays to large vs small bin operations */
-
-#define insert_chunk(M, P, S)\
-  if (is_small(S)) insert_small_chunk(M, P, S)\
-  else { tchunkptr TP = (tchunkptr)(P); insert_large_chunk(M, TP, S); }
-
-#define unlink_chunk(M, P, S)\
-  if (is_small(S)) unlink_small_chunk(M, P, S)\
-  else { tchunkptr TP = (tchunkptr)(P); unlink_large_chunk(M, TP); }
-
-
-/* Relays to internal calls to malloc/free from realloc, memalign etc */
-
-#if ONLY_MSPACES
-#define internal_malloc(m, b) mspace_malloc(m, b)
-#define internal_free(m, mem) mspace_free(m,mem);
-#else /* ONLY_MSPACES */
-#if MSPACES
-#define internal_malloc(m, b)\
-   (m == gm)? dlmalloc(b) : mspace_malloc(m, b)
-#define internal_free(m, mem)\
-   if (m == gm) dlfree(mem); else mspace_free(m,mem);
-#else /* MSPACES */
-#define internal_malloc(m, b) dlmalloc(b)
-#define internal_free(m, mem) dlfree(mem)
-#endif /* MSPACES */
-#endif /* ONLY_MSPACES */
-
-/* -----------------------  Direct-mmapping chunks ----------------------- */
-
-/*
-  Directly mmapped chunks are set up with an offset to the start of
-  the mmapped region stored in the prev_foot field of the chunk. This
-  allows reconstruction of the required argument to MUNMAP when freed,
-  and also allows adjustment of the returned chunk to meet alignment
-  requirements (especially in memalign).  There is also enough space
-  allocated to hold a fake next chunk of size SIZE_T_SIZE to maintain
-  the PINUSE bit so frees can be checked.
-*/
-
-/* Malloc using mmap */
-static void* mmap_alloc(mstate m, size_t nb) {
-  size_t mmsize = mmap_align(nb + SIX_SIZE_T_SIZES + CHUNK_ALIGN_MASK);
-  if (mmsize > nb) {     /* Check for wrap around 0 */
-    char* mm = (char*)(CALL_DIRECT_MMAP(mmsize));
-    if (mm != CMFAIL) {
-      size_t offset = align_offset(chunk2mem(mm));
-      size_t psize = mmsize - offset - MMAP_FOOT_PAD;
-      mchunkptr p = (mchunkptr)(mm + offset);
-      p->prev_foot = offset | IS_MMAPPED_BIT;
-      (p)->head = (psize|CINUSE_BIT);
-      mark_inuse_foot(m, p, psize);
-      chunk_plus_offset(p, psize)->head = FENCEPOST_HEAD;
-      chunk_plus_offset(p, psize+SIZE_T_SIZE)->head = 0;
-
-      if (mm < m->least_addr)
-	m->least_addr = mm;
-      if ((m->footprint += mmsize) > m->max_footprint)
-	m->max_footprint = m->footprint;
-      assert(is_aligned(chunk2mem(p)));
-      check_mmapped_chunk(m, p);
-      return chunk2mem(p);
-    }
-  }
-  return 0;
-}
-
-/* Realloc using mmap */
-static mchunkptr mmap_resize(mstate m, mchunkptr oldp, size_t nb) {
-  size_t oldsize = chunksize(oldp);
-  if (is_small(nb)) /* Can't shrink mmap regions below small size */
-    return 0;
-  /* Keep old chunk if big enough but not too big */
-  if (oldsize >= nb + SIZE_T_SIZE &&
-      (oldsize - nb) <= (mparams.granularity << 1))
-    return oldp;
-  else {
-    size_t offset = oldp->prev_foot & ~IS_MMAPPED_BIT;
-    size_t oldmmsize = oldsize + offset + MMAP_FOOT_PAD;
-    size_t newmmsize = mmap_align(nb + SIX_SIZE_T_SIZES + CHUNK_ALIGN_MASK);
-    char* cp = (char*)CALL_MREMAP((char*)oldp - offset,
-				  oldmmsize, newmmsize, 1);
-    if (cp != CMFAIL) {
-      mchunkptr newp = (mchunkptr)(cp + offset);
-      size_t psize = newmmsize - offset - MMAP_FOOT_PAD;
-      newp->head = (psize|CINUSE_BIT);
-      mark_inuse_foot(m, newp, psize);
-      chunk_plus_offset(newp, psize)->head = FENCEPOST_HEAD;
-      chunk_plus_offset(newp, psize+SIZE_T_SIZE)->head = 0;
-
-      if (cp < m->least_addr)
-	m->least_addr = cp;
-      if ((m->footprint += newmmsize - oldmmsize) > m->max_footprint)
-	m->max_footprint = m->footprint;
-      check_mmapped_chunk(m, newp);
-      return newp;
-    }
-  }
-  return 0;
-}
-
-/* -------------------------- mspace management -------------------------- */
-
-/* Initialize top chunk and its size */
-static void init_top(mstate m, mchunkptr p, size_t psize) {
-  /* Ensure alignment */
-  size_t offset = align_offset(chunk2mem(p));
-  p = (mchunkptr)((char*)p + offset);
-  psize -= offset;
-
-  m->top = p;
-  m->topsize = psize;
-  p->head = psize | PINUSE_BIT;
-  /* set size of fake trailing chunk holding overhead space only once */
-  chunk_plus_offset(p, psize)->head = TOP_FOOT_SIZE;
-  m->trim_check = mparams.trim_threshold; /* reset on each update */
-}
-
-/* Initialize bins for a new mstate that is otherwise zeroed out */
-static void init_bins(mstate m) {
-  /* Establish circular links for smallbins */
-  bindex_t i;
-  for (i = 0; i < NSMALLBINS; ++i) {
-    sbinptr bin = smallbin_at(m,i);
-    bin->fd = bin->bk = bin;
-  }
-}
-
-#if PROCEED_ON_ERROR
-
-/* default corruption action */
-static void reset_on_error(mstate m) {
-  int i;
-  ++malloc_corruption_error_count;
-  /* Reinitialize fields to forget about all memory */
-  m->smallbins = m->treebins = 0;
-  m->dvsize = m->topsize = 0;
-  m->seg.base = 0;
-  m->seg.size = 0;
-  m->seg.next = 0;
-  m->top = m->dv = 0;
-  for (i = 0; i < NTREEBINS; ++i)
-    *treebin_at(m, i) = 0;
-  init_bins(m);
-}
-#endif /* PROCEED_ON_ERROR */
-
-/* Allocate chunk and prepend remainder with chunk in successor base. */
-static void* prepend_alloc(mstate m, char* newbase, char* oldbase,
-			   size_t nb) {
-  mchunkptr p = align_as_chunk(newbase);
-  mchunkptr oldfirst = align_as_chunk(oldbase);
-  size_t psize = (char*)oldfirst - (char*)p;
-  mchunkptr q = chunk_plus_offset(p, nb);
-  size_t qsize = psize - nb;
-  set_size_and_pinuse_of_inuse_chunk(m, p, nb);
-
-  assert((char*)oldfirst > (char*)q);
-  assert(pinuse(oldfirst));
-  assert(qsize >= MIN_CHUNK_SIZE);
-
-  /* consolidate remainder with first chunk of old base */
-  if (oldfirst == m->top) {
-    size_t tsize = m->topsize += qsize;
-    m->top = q;
-    q->head = tsize | PINUSE_BIT;
-    check_top_chunk(m, q);
-  }
-  else if (oldfirst == m->dv) {
-    size_t dsize = m->dvsize += qsize;
-    m->dv = q;
-    set_size_and_pinuse_of_free_chunk(q, dsize);
-  }
-  else {
-    if (!cinuse(oldfirst)) {
-      size_t nsize = chunksize(oldfirst);
-      unlink_chunk(m, oldfirst, nsize);
-      oldfirst = chunk_plus_offset(oldfirst, nsize);
-      qsize += nsize;
-    }
-    set_free_with_pinuse(q, qsize, oldfirst);
-    insert_chunk(m, q, qsize);
-    check_free_chunk(m, q);
-  }
-
-  check_malloced_chunk(m, chunk2mem(p), nb);
-  return chunk2mem(p);
-}
-
-/* Add a segment to hold a new noncontiguous region */
-static void add_segment(mstate m, char* tbase, size_t tsize, flag_t mmapped) {
-  /* Determine locations and sizes of segment, fenceposts, old top */
-  char* old_top = (char*)m->top;
-  msegmentptr oldsp = segment_holding(m, old_top);
-  char* old_end = oldsp->base + oldsp->size;
-  size_t ssize = pad_request(sizeof(struct malloc_segment));
-  char* rawsp = old_end - (ssize + FOUR_SIZE_T_SIZES + CHUNK_ALIGN_MASK);
-  size_t offset = align_offset(chunk2mem(rawsp));
-  char* asp = rawsp + offset;
-  char* csp = (asp < (old_top + MIN_CHUNK_SIZE))? old_top : asp;
-  mchunkptr sp = (mchunkptr)csp;
-  msegmentptr ss = (msegmentptr)(chunk2mem(sp));
-  mchunkptr tnext = chunk_plus_offset(sp, ssize);
-  mchunkptr p = tnext;
-  int nfences = 0;
-
-  /* reset top to new space */
-  init_top(m, (mchunkptr)tbase, tsize - TOP_FOOT_SIZE);
-
-  /* Set up segment record */
-  assert(is_aligned(ss));
-  set_size_and_pinuse_of_inuse_chunk(m, sp, ssize);
-  *ss = m->seg; /* Push current record */
-  m->seg.base = tbase;
-  m->seg.size = tsize;
-  m->seg.sflags = mmapped;
-  m->seg.next = ss;
-
-  /* Insert trailing fenceposts */
-  for (;;) {
-    mchunkptr nextp = chunk_plus_offset(p, SIZE_T_SIZE);
-    p->head = FENCEPOST_HEAD;
-    ++nfences;
-    if ((char*)(&(nextp->head)) < old_end)
-      p = nextp;
-    else
-      break;
-  }
-  assert(nfences >= 2);
-
-  /* Insert the rest of old top into a bin as an ordinary free chunk */
-  if (csp != old_top) {
-    mchunkptr q = (mchunkptr)old_top;
-    size_t psize = csp - old_top;
-    mchunkptr tn = chunk_plus_offset(q, psize);
-    set_free_with_pinuse(q, psize, tn);
-    insert_chunk(m, q, psize);
-  }
-
-  check_top_chunk(m, m->top);
-}
-
-/* -------------------------- System allocation -------------------------- */
-
-/* Get memory from system using MORECORE or MMAP */
-static void* sys_alloc(mstate m, size_t nb) {
-  char* tbase = CMFAIL;
-  size_t tsize = 0;
-  flag_t mmap_flag = 0;
-
-  ensure_initialization();
-
-  /* Directly map large chunks */
-  if (use_mmap(m) && nb >= mparams.mmap_threshold) {
-    void* mem = mmap_alloc(m, nb);
-    if (mem != 0)
-      return mem;
-  }
-
-  /*
-    Try getting memory in any of three ways (in most-preferred to
-    least-preferred order):
-    1. A call to MORECORE that can normally contiguously extend memory.
-       (disabled if not MORECORE_CONTIGUOUS or not HAVE_MORECORE or
-       main space is mmapped or a previous contiguous call failed)
-    2. A call to MMAP new space (disabled if not HAVE_MMAP).
-       Note that under the default settings, if MORECORE is unable to
-       fulfill a request, and HAVE_MMAP is true, then mmap is
-       used as a noncontiguous system allocator. This is a useful backup
-       strategy for systems with holes in address spaces -- in this case
-       sbrk cannot contiguously expand the heap, but mmap may be able to
-       find space.
-    3. A call to MORECORE that cannot usually contiguously extend memory.
-       (disabled if not HAVE_MORECORE)
-
-   In all cases, we need to request enough bytes from system to ensure
-   we can malloc nb bytes upon success, so pad with enough space for
-   top_foot, plus alignment-pad to make sure we don't lose bytes if
-   not on boundary, and round this up to a granularity unit.
-  */
-
-  if (MORECORE_CONTIGUOUS && !use_noncontiguous(m)) {
-    char* br = CMFAIL;
-    msegmentptr ss = (m->top == 0)? 0 : segment_holding(m, (char*)m->top);
-    size_t asize = 0;
-    ACQUIRE_MALLOC_GLOBAL_LOCK();
-
-    if (ss == 0) {  /* First time through or recovery */
-      char* base = (char*)CALL_MORECORE(0);
-      if (base != CMFAIL) {
-	asize = granularity_align(nb + SYS_ALLOC_PADDING);
-	/* Adjust to end on a page boundary */
-	if (!is_page_aligned(base))
-	  asize += (page_align((size_t)base) - (size_t)base);
-	/* Can't call MORECORE if size is negative when treated as signed */
-	if (asize < HALF_MAX_SIZE_T &&
-	    (br = (char*)(CALL_MORECORE(asize))) == base) {
-	  tbase = base;
-	  tsize = asize;
-	}
-      }
-    }
-    else {
-      /* Subtract out existing available top space from MORECORE request. */
-      asize = granularity_align(nb - m->topsize + SYS_ALLOC_PADDING);
-      /* Use mem here only if it did continuously extend old space */
-      if (asize < HALF_MAX_SIZE_T &&
-	  (br = (char*)(CALL_MORECORE(asize))) == ss->base+ss->size) {
-	tbase = br;
-	tsize = asize;
-      }
-    }
-
-    if (tbase == CMFAIL) {    /* Cope with partial failure */
-      if (br != CMFAIL) {    /* Try to use/extend the space we did get */
-	if (asize < HALF_MAX_SIZE_T &&
-	    asize < nb + SYS_ALLOC_PADDING) {
-	  size_t esize = granularity_align(nb + SYS_ALLOC_PADDING - asize);
-	  if (esize < HALF_MAX_SIZE_T) {
-	    char* end = (char*)CALL_MORECORE(esize);
-	    if (end != CMFAIL)
-	      asize += esize;
-	    else {            /* Can't use; try to release */
-	      (void) CALL_MORECORE(-asize);
-	      br = CMFAIL;
-	    }
-	  }
-	}
-      }
-      if (br != CMFAIL) {    /* Use the space we did get */
-	tbase = br;
-	tsize = asize;
-      }
-      else
-	disable_contiguous(m); /* Don't try contiguous path in the future */
-    }
-
-    RELEASE_MALLOC_GLOBAL_LOCK();
-  }
-
-  if (HAVE_MMAP && tbase == CMFAIL) {  /* Try MMAP */
-    size_t rsize = granularity_align(nb + SYS_ALLOC_PADDING);
-    if (rsize > nb) { /* Fail if wraps around zero */
-      char* mp = (char*)(CALL_MMAP(rsize));
-      if (mp != CMFAIL) {
-	tbase = mp;
-	tsize = rsize;
-	mmap_flag = IS_MMAPPED_BIT;
-      }
-    }
-  }
-
-  if (HAVE_MORECORE && tbase == CMFAIL) { /* Try noncontiguous MORECORE */
-    size_t asize = granularity_align(nb + SYS_ALLOC_PADDING);
-    if (asize < HALF_MAX_SIZE_T) {
-      char* br = CMFAIL;
-      char* end = CMFAIL;
-      ACQUIRE_MALLOC_GLOBAL_LOCK();
-      br = (char*)(CALL_MORECORE(asize));
-      end = (char*)(CALL_MORECORE(0));
-      RELEASE_MALLOC_GLOBAL_LOCK();
-      if (br != CMFAIL && end != CMFAIL && br < end) {
-	size_t ssize = end - br;
-	if (ssize > nb + TOP_FOOT_SIZE) {
-	  tbase = br;
-	  tsize = ssize;
-	}
-      }
-    }
-  }
-
-  if (tbase != CMFAIL) {
-
-    if ((m->footprint += tsize) > m->max_footprint)
-      m->max_footprint = m->footprint;
-
-    if (!is_initialized(m)) { /* first-time initialization */
-      m->seg.base = m->least_addr = tbase;
-      m->seg.size = tsize;
-      m->seg.sflags = mmap_flag;
-      m->magic = mparams.magic;
-      m->release_checks = MAX_RELEASE_CHECK_RATE;
-      init_bins(m);
-#if !ONLY_MSPACES
-      if (is_global(m))
-	init_top(m, (mchunkptr)tbase, tsize - TOP_FOOT_SIZE);
-      else
-#endif
-      {
-	/* Offset top by embedded malloc_state */
-	mchunkptr mn = next_chunk(mem2chunk(m));
-	init_top(m, mn, (size_t)((tbase + tsize) - (char*)mn) -TOP_FOOT_SIZE);
-      }
-    }
-
-    else {
-      /* Try to merge with an existing segment */
-      msegmentptr sp = &m->seg;
-      /* Only consider most recent segment if traversal suppressed */
-      while (sp != 0 && tbase != sp->base + sp->size)
-	sp = (NO_SEGMENT_TRAVERSAL) ? 0 : sp->next;
-      if (sp != 0 &&
-	  !is_extern_segment(sp) &&
-	  (sp->sflags & IS_MMAPPED_BIT) == mmap_flag &&
-	  segment_holds(sp, m->top)) { /* append */
-	sp->size += tsize;
-	init_top(m, m->top, m->topsize + tsize);
-      }
-      else {
-	if (tbase < m->least_addr)
-	  m->least_addr = tbase;
-	sp = &m->seg;
-	while (sp != 0 && sp->base != tbase + tsize)
-	  sp = (NO_SEGMENT_TRAVERSAL) ? 0 : sp->next;
-	if (sp != 0 &&
-	    !is_extern_segment(sp) &&
-	    (sp->sflags & IS_MMAPPED_BIT) == mmap_flag) {
-	  char* oldbase = sp->base;
-	  sp->base = tbase;
-	  sp->size += tsize;
-	  return prepend_alloc(m, tbase, oldbase, nb);
-	}
-	else
-	  add_segment(m, tbase, tsize, mmap_flag);
-      }
-    }
-
-    if (nb < m->topsize) { /* Allocate from new or extended top space */
-      size_t rsize = m->topsize -= nb;
-      mchunkptr p = m->top;
-      mchunkptr r = m->top = chunk_plus_offset(p, nb);
-      r->head = rsize | PINUSE_BIT;
-      set_size_and_pinuse_of_inuse_chunk(m, p, nb);
-      check_top_chunk(m, m->top);
-      check_malloced_chunk(m, chunk2mem(p), nb);
-      return chunk2mem(p);
-    }
-  }
-
-  MALLOC_FAILURE_ACTION;
-  return 0;
-}
-
-/* -----------------------  system deallocation -------------------------- */
-
-/* Unmap and unlink any mmapped segments that don't contain used chunks */
-static size_t release_unused_segments(mstate m) {
-  size_t released = 0;
-  int nsegs = 0;
-  msegmentptr pred = &m->seg;
-  msegmentptr sp = pred->next;
-  while (sp != 0) {
-    char* base = sp->base;
-    size_t size = sp->size;
-    msegmentptr next = sp->next;
-    ++nsegs;
-    if (is_mmapped_segment(sp) && !is_extern_segment(sp)) {
-      mchunkptr p = align_as_chunk(base);
-      size_t psize = chunksize(p);
-      /* Can unmap if first chunk holds entire segment and not pinned */
-      if (!cinuse(p) && (char*)p + psize >= base + size - TOP_FOOT_SIZE) {
-	tchunkptr tp = (tchunkptr)p;
-	assert(segment_holds(sp, (char*)sp));
-	if (p == m->dv) {
-	  m->dv = 0;
-	  m->dvsize = 0;
-	}
-	else {
-	  unlink_large_chunk(m, tp);
-	}
-	if (CALL_MUNMAP(base, size) == 0) {
-	  released += size;
-	  m->footprint -= size;
-	  /* unlink obsoleted record */
-	  sp = pred;
-	  sp->next = next;
-	}
-	else { /* back out if cannot unmap */
-	  insert_large_chunk(m, tp, psize);
-	}
-      }
-    }
-    if (NO_SEGMENT_TRAVERSAL) /* scan only first segment */
-      break;
-    pred = sp;
-    sp = next;
-  }
-  /* Reset check counter */
-  m->release_checks = ((nsegs > MAX_RELEASE_CHECK_RATE)?
-		       nsegs : MAX_RELEASE_CHECK_RATE);
-  return released;
-}
-
-static int sys_trim(mstate m, size_t pad) {
-  size_t released = 0;
-  ensure_initialization();
-  if (pad < MAX_REQUEST && is_initialized(m)) {
-    pad += TOP_FOOT_SIZE; /* ensure enough room for segment overhead */
-
-    if (m->topsize > pad) {
-      /* Shrink top space in granularity-size units, keeping at least one */
-      size_t unit = mparams.granularity;
-      size_t extra = ((m->topsize - pad + (unit - SIZE_T_ONE)) / unit -
-		      SIZE_T_ONE) * unit;
-      msegmentptr sp = segment_holding(m, (char*)m->top);
-
-      if (!is_extern_segment(sp)) {
-	if (is_mmapped_segment(sp)) {
-	  if (HAVE_MMAP &&
-	      sp->size >= extra &&
-	      !has_segment_link(m, sp)) { /* can't shrink if pinned */
-	    size_t newsize = sp->size - extra;
-	    /* Prefer mremap, fall back to munmap */
-	    if ((CALL_MREMAP(sp->base, sp->size, newsize, 0) != MFAIL) ||
-		(CALL_MUNMAP(sp->base + newsize, extra) == 0)) {
-	      released = extra;
-	    }
-	  }
-	}
-	else if (HAVE_MORECORE) {
-	  if (extra >= HALF_MAX_SIZE_T) /* Avoid wrapping negative */
-	    extra = (HALF_MAX_SIZE_T) + SIZE_T_ONE - unit;
-	  ACQUIRE_MALLOC_GLOBAL_LOCK();
-	  {
-	    /* Make sure end of memory is where we last set it. */
-	    char* old_br = (char*)(CALL_MORECORE(0));
-	    if (old_br == sp->base + sp->size) {
-	      char* rel_br = (char*)(CALL_MORECORE(-extra));
-	      char* new_br = (char*)(CALL_MORECORE(0));
-	      if (rel_br != CMFAIL && new_br < old_br)
-		released = old_br - new_br;
-	    }
-	  }
-	  RELEASE_MALLOC_GLOBAL_LOCK();
-	}
-      }
-
-      if (released != 0) {
-	sp->size -= released;
-	m->footprint -= released;
-	init_top(m, m->top, m->topsize - released);
-	check_top_chunk(m, m->top);
-      }
-    }
-
-    /* Unmap any unused mmapped segments */
-    if (HAVE_MMAP)
-      released += release_unused_segments(m);
-
-    /* On failure, disable autotrim to avoid repeated failed future calls */
-    if (released == 0 && m->topsize > m->trim_check)
-      m->trim_check = MAX_SIZE_T;
-  }
-
-  return (released != 0)? 1 : 0;
-}
-
-
-/* ---------------------------- malloc support --------------------------- */
-
-/* allocate a large request from the best fitting chunk in a treebin */
-static void* tmalloc_large(mstate m, size_t nb) {
-  tchunkptr v = 0;
-  size_t rsize = -nb; /* Unsigned negation */
-  tchunkptr t;
-  bindex_t idx;
-  compute_tree_index(nb, idx);
-  if ((t = *treebin_at(m, idx)) != 0) {
-    /* Traverse tree for this bin looking for node with size == nb */
-    size_t sizebits = nb << leftshift_for_tree_index(idx);
-    tchunkptr rst = 0;  /* The deepest untaken right subtree */
-    for (;;) {
-      tchunkptr rt;
-      size_t trem = chunksize(t) - nb;
-      if (trem < rsize) {
-	v = t;
-	if ((rsize = trem) == 0)
-	  break;
-      }
-      rt = t->child[1];
-      t = t->child[(sizebits >> (SIZE_T_BITSIZE-SIZE_T_ONE)) & 1];
-      if (rt != 0 && rt != t)
-	rst = rt;
-      if (t == 0) {
-	t = rst; /* set t to least subtree holding sizes > nb */
-	break;
-      }
-      sizebits <<= 1;
-    }
-  }
-  if (t == 0 && v == 0) { /* set t to root of next non-empty treebin */
-    binmap_t leftbits = left_bits(idx2bit(idx)) & m->treemap;
-    if (leftbits != 0) {
-      bindex_t i;
-      binmap_t leastbit = least_bit(leftbits);
-      compute_bit2idx(leastbit, i);
-      t = *treebin_at(m, i);
-    }
-  }
-
-  while (t != 0) { /* find smallest of tree or subtree */
-    size_t trem = chunksize(t) - nb;
-    if (trem < rsize) {
-      rsize = trem;
-      v = t;
-    }
-    t = leftmost_child(t);
-  }
-
-  /*  If dv is a better fit, return 0 so malloc will use it */
-  if (v != 0 && rsize < (size_t)(m->dvsize - nb)) {
-    if (RTCHECK(ok_address(m, v))) { /* split */
-      mchunkptr r = chunk_plus_offset(v, nb);
-      assert(chunksize(v) == rsize + nb);
-      if (RTCHECK(ok_next(v, r))) {
-	unlink_large_chunk(m, v);
-	if (rsize < MIN_CHUNK_SIZE)
-	  set_inuse_and_pinuse(m, v, (rsize + nb));
-	else {
-	  set_size_and_pinuse_of_inuse_chunk(m, v, nb);
-	  set_size_and_pinuse_of_free_chunk(r, rsize);
-	  insert_chunk(m, r, rsize);
-	}
-	return chunk2mem(v);
-      }
-    }
-    CORRUPTION_ERROR_ACTION(m);
-  }
-  return 0;
-}
-
-/* allocate a small request from the best fitting chunk in a treebin */
-static void* tmalloc_small(mstate m, size_t nb) {
-  tchunkptr t, v;
-  size_t rsize;
-  bindex_t i;
-  binmap_t leastbit = least_bit(m->treemap);
-  compute_bit2idx(leastbit, i);
-  v = t = *treebin_at(m, i);
-  rsize = chunksize(t) - nb;
-
-  while ((t = leftmost_child(t)) != 0) {
-    size_t trem = chunksize(t) - nb;
-    if (trem < rsize) {
-      rsize = trem;
-      v = t;
-    }
-  }
-
-  if (RTCHECK(ok_address(m, v))) {
-    mchunkptr r = chunk_plus_offset(v, nb);
-    assert(chunksize(v) == rsize + nb);
-    if (RTCHECK(ok_next(v, r))) {
-      unlink_large_chunk(m, v);
-      if (rsize < MIN_CHUNK_SIZE)
-	set_inuse_and_pinuse(m, v, (rsize + nb));
-      else {
-	set_size_and_pinuse_of_inuse_chunk(m, v, nb);
-	set_size_and_pinuse_of_free_chunk(r, rsize);
-	replace_dv(m, r, rsize);
-      }
-      return chunk2mem(v);
-    }
-  }
-
-  CORRUPTION_ERROR_ACTION(m);
-  return 0;
-}
-
-/* --------------------------- realloc support --------------------------- */
-
-static void* internal_realloc(mstate m, void* oldmem, size_t bytes) {
-  if (bytes >= MAX_REQUEST) {
-    MALLOC_FAILURE_ACTION;
-    return 0;
-  }
-  if (!PREACTION(m)) {
-    mchunkptr oldp = mem2chunk(oldmem);
-    size_t oldsize = chunksize(oldp);
-    mchunkptr next = chunk_plus_offset(oldp, oldsize);
-    mchunkptr newp = 0;
-    void* extra = 0;
-
-    /* Try to either shrink or extend into top. Else malloc-copy-free */
-
-    if (RTCHECK(ok_address(m, oldp) && ok_cinuse(oldp) &&
-		ok_next(oldp, next) && ok_pinuse(next))) {
-      size_t nb = request2size(bytes);
-      if (is_mmapped(oldp))
-	newp = mmap_resize(m, oldp, nb);
-      else if (oldsize >= nb) { /* already big enough */
-	size_t rsize = oldsize - nb;
-	newp = oldp;
-	if (rsize >= MIN_CHUNK_SIZE) {
-	  mchunkptr remainder = chunk_plus_offset(newp, nb);
-	  set_inuse(m, newp, nb);
-	  set_inuse(m, remainder, rsize);
-	  extra = chunk2mem(remainder);
-	}
-      }
-      else if (next == m->top && oldsize + m->topsize > nb) {
-	/* Expand into top */
-	size_t newsize = oldsize + m->topsize;
-	size_t newtopsize = newsize - nb;
-	mchunkptr newtop = chunk_plus_offset(oldp, nb);
-	set_inuse(m, oldp, nb);
-	newtop->head = newtopsize |PINUSE_BIT;
-	m->top = newtop;
-	m->topsize = newtopsize;
-	newp = oldp;
-      }
-    }
-    else {
-      USAGE_ERROR_ACTION(m, oldmem);
-      POSTACTION(m);
-      return 0;
-    }
-
-    POSTACTION(m);
-
-    if (newp != 0) {
-      if (extra != 0) {
-	internal_free(m, extra);
-      }
-      check_inuse_chunk(m, newp);
-      return chunk2mem(newp);
-    }
-    else {
-      void* newmem = internal_malloc(m, bytes);
-      if (newmem != 0) {
-	size_t oc = oldsize - overhead_for(oldp);
-	memcpy(newmem, oldmem, (oc < bytes)? oc : bytes);
-	internal_free(m, oldmem);
-      }
-      return newmem;
-    }
-  }
-  return 0;
-}
-
-/* --------------------------- memalign support -------------------------- */
-
-static void* internal_memalign(mstate m, size_t alignment, size_t bytes) {
-  if (alignment <= MALLOC_ALIGNMENT)    /* Can just use malloc */
-    return internal_malloc(m, bytes);
-  if (alignment <  MIN_CHUNK_SIZE) /* must be at least a minimum chunk size */
-    alignment = MIN_CHUNK_SIZE;
-  if ((alignment & (alignment-SIZE_T_ONE)) != 0) {/* Ensure a power of 2 */
-    size_t a = MALLOC_ALIGNMENT << 1;
-    while (a < alignment) a <<= 1;
-    alignment = a;
-  }
-
-  if (bytes >= MAX_REQUEST - alignment) {
-    if (m != 0)  { /* Test isn't needed but avoids compiler warning */
-      MALLOC_FAILURE_ACTION;
-    }
-  }
-  else {
-    size_t nb = request2size(bytes);
-    size_t req = nb + alignment + MIN_CHUNK_SIZE - CHUNK_OVERHEAD;
-    char* mem = (char*)internal_malloc(m, req);
-    if (mem != 0) {
-      void* leader = 0;
-      void* trailer = 0;
-      mchunkptr p = mem2chunk(mem);
-
-      if (PREACTION(m)) return 0;
-      if ((((size_t)(mem)) % alignment) != 0) { /* misaligned */
-	/*
-	  Find an aligned spot inside chunk.  Since we need to give
-	  back leading space in a chunk of at least MIN_CHUNK_SIZE, if
-	  the first calculation places us at a spot with less than
-	  MIN_CHUNK_SIZE leader, we can move to the next aligned spot.
-	  We've allocated enough total room so that this is always
-	  possible.
-	*/
-	char* br = (char*)mem2chunk((size_t)(((size_t)(mem +
-						       alignment -
-						       SIZE_T_ONE)) &
-					     -alignment));
-	char* pos = ((size_t)(br - (char*)(p)) >= MIN_CHUNK_SIZE)?
-	  br : br+alignment;
-	mchunkptr newp = (mchunkptr)pos;
-	size_t leadsize = pos - (char*)(p);
-	size_t newsize = chunksize(p) - leadsize;
-
-	if (is_mmapped(p)) { /* For mmapped chunks, just adjust offset */
-	  newp->prev_foot = p->prev_foot + leadsize;
-	  newp->head = (newsize|CINUSE_BIT);
-	}
-	else { /* Otherwise, give back leader, use the rest */
-	  set_inuse(m, newp, newsize);
-	  set_inuse(m, p, leadsize);
-	  leader = chunk2mem(p);
-	}
-	p = newp;
-      }
-
-      /* Give back spare room at the end */
-      if (!is_mmapped(p)) {
-	size_t size = chunksize(p);
-	if (size > nb + MIN_CHUNK_SIZE) {
-	  size_t remainder_size = size - nb;
-	  mchunkptr remainder = chunk_plus_offset(p, nb);
-	  set_inuse(m, p, nb);
-	  set_inuse(m, remainder, remainder_size);
-	  trailer = chunk2mem(remainder);
-	}
-      }
-
-      assert (chunksize(p) >= nb);
-      assert((((size_t)(chunk2mem(p))) % alignment) == 0);
-      check_inuse_chunk(m, p);
-      POSTACTION(m);
-      if (leader != 0) {
-	internal_free(m, leader);
-      }
-      if (trailer != 0) {
-	internal_free(m, trailer);
-      }
-      return chunk2mem(p);
-    }
-  }
-  return 0;
-}
-
-/* ------------------------ comalloc/coalloc support --------------------- */
-
-static void** ialloc(mstate m,
-		     size_t n_elements,
-		     size_t* sizes,
-		     int opts,
-		     void* chunks[]) {
-  /*
-    This provides common support for independent_X routines, handling
-    all of the combinations that can result.
-
-    The opts arg has:
-    bit 0 set if all elements are same size (using sizes[0])
-    bit 1 set if elements should be zeroed
-  */
-
-  size_t    element_size;   /* chunksize of each element, if all same */
-  size_t    contents_size;  /* total size of elements */
-  size_t    array_size;     /* request size of pointer array */
-  void*     mem;            /* malloced aggregate space */
-  mchunkptr p;              /* corresponding chunk */
-  size_t    remainder_size; /* remaining bytes while splitting */
-  void**    marray;         /* either "chunks" or malloced ptr array */
-  mchunkptr array_chunk;    /* chunk for malloced ptr array */
-  flag_t    was_enabled;    /* to disable mmap */
-  size_t    size;
-  size_t    i;
-
-  ensure_initialization();
-  /* compute array length, if needed */
-  if (chunks != 0) {
-    if (n_elements == 0)
-      return chunks; /* nothing to do */
-    marray = chunks;
-    array_size = 0;
-  }
-  else {
-    /* if empty req, must still return chunk representing empty array */
-    if (n_elements == 0)
-      return (void**)internal_malloc(m, 0);
-    marray = 0;
-    array_size = request2size(n_elements * (sizeof(void*)));
-  }
-
-  /* compute total element size */
-  if (opts & 0x1) { /* all-same-size */
-    element_size = request2size(*sizes);
-    contents_size = n_elements * element_size;
-  }
-  else { /* add up all the sizes */
-    element_size = 0;
-    contents_size = 0;
-    for (i = 0; i != n_elements; ++i)
-      contents_size += request2size(sizes[i]);
-  }
-
-  size = contents_size + array_size;
-
-  /*
-     Allocate the aggregate chunk.  First disable direct-mmapping so
-     malloc won't use it, since we would not be able to later
-     free/realloc space internal to a segregated mmap region.
-  */
-  was_enabled = use_mmap(m);
-  disable_mmap(m);
-  mem = internal_malloc(m, size - CHUNK_OVERHEAD);
-  if (was_enabled)
-    enable_mmap(m);
-  if (mem == 0)
-    return 0;
-
-  if (PREACTION(m)) return 0;
-  p = mem2chunk(mem);
-  remainder_size = chunksize(p);
-
-  assert(!is_mmapped(p));
-
-  if (opts & 0x2) {       /* optionally clear the elements */
-    memset((size_t*)mem, 0, remainder_size - SIZE_T_SIZE - array_size);
-  }
-
-  /* If not provided, allocate the pointer array as final part of chunk */
-  if (marray == 0) {
-    size_t  array_chunk_size;
-    array_chunk = chunk_plus_offset(p, contents_size);
-    array_chunk_size = remainder_size - contents_size;
-    marray = (void**) (chunk2mem(array_chunk));
-    set_size_and_pinuse_of_inuse_chunk(m, array_chunk, array_chunk_size);
-    remainder_size = contents_size;
-  }
-
-  /* split out elements */
-  for (i = 0; ; ++i) {
-    marray[i] = chunk2mem(p);
-    if (i != n_elements-1) {
-      if (element_size != 0)
-	size = element_size;
-      else
-	size = request2size(sizes[i]);
-      remainder_size -= size;
-      set_size_and_pinuse_of_inuse_chunk(m, p, size);
-      p = chunk_plus_offset(p, size);
-    }
-    else { /* the final element absorbs any overallocation slop */
-      set_size_and_pinuse_of_inuse_chunk(m, p, remainder_size);
-      break;
-    }
-  }
-
-#if DEBUG
-  if (marray != chunks) {
-    /* final element must have exactly exhausted chunk */
-    if (element_size != 0) {
-      assert(remainder_size == element_size);
-    }
-    else {
-      assert(remainder_size == request2size(sizes[i]));
-    }
-    check_inuse_chunk(m, mem2chunk(marray));
-  }
-  for (i = 0; i != n_elements; ++i)
-    check_inuse_chunk(m, mem2chunk(marray[i]));
-
-#endif /* DEBUG */
-
-  POSTACTION(m);
-  return marray;
-}
-
-
-/* -------------------------- public routines ---------------------------- */
-
-#if !ONLY_MSPACES
-
-void* dlmalloc(size_t bytes) {
-  /*
-     Basic algorithm:
-     If a small request (< 256 bytes minus per-chunk overhead):
-       1. If one exists, use a remainderless chunk in associated smallbin.
-	  (Remainderless means that there are too few excess bytes to
-	  represent as a chunk.)
-       2. If it is big enough, use the dv chunk, which is normally the
-	  chunk adjacent to the one used for the most recent small request.
-       3. If one exists, split the smallest available chunk in a bin,
-	  saving remainder in dv.
-       4. If it is big enough, use the top chunk.
-       5. If available, get memory from system and use it
-     Otherwise, for a large request:
-       1. Find the smallest available binned chunk that fits, and use it
-	  if it is better fitting than dv chunk, splitting if necessary.
-       2. If better fitting than any binned chunk, use the dv chunk.
-       3. If it is big enough, use the top chunk.
-       4. If request size >= mmap threshold, try to directly mmap this chunk.
-       5. If available, get memory from system and use it
-
-     The ugly goto's here ensure that postaction occurs along all paths.
-  */
-
-#if USE_LOCKS
-  ensure_initialization(); /* initialize in sys_alloc if not using locks */
-#endif
-
-  if (!PREACTION(gm)) {
-    void* mem;
-    size_t nb;
-    if (bytes <= MAX_SMALL_REQUEST) {
-      bindex_t idx;
-      binmap_t smallbits;
-      nb = (bytes < MIN_REQUEST)? MIN_CHUNK_SIZE : pad_request(bytes);
-      idx = small_index(nb);
-      smallbits = gm->smallmap >> idx;
-
-      if ((smallbits & 0x3U) != 0) { /* Remainderless fit to a smallbin. */
-	mchunkptr b, p;
-	idx += ~smallbits & 1;       /* Uses next bin if idx empty */
-	b = smallbin_at(gm, idx);
-	p = b->fd;
-	assert(chunksize(p) == small_index2size(idx));
-	unlink_first_small_chunk(gm, b, p, idx);
-	set_inuse_and_pinuse(gm, p, small_index2size(idx));
-	mem = chunk2mem(p);
-	check_malloced_chunk(gm, mem, nb);
-	goto postaction;
-      }
-
-      else if (nb > gm->dvsize) {
-	if (smallbits != 0) { /* Use chunk in next nonempty smallbin */
-	  mchunkptr b, p, r;
-	  size_t rsize;
-	  bindex_t i;
-	  binmap_t leftbits = (smallbits << idx) & left_bits(idx2bit(idx));
-	  binmap_t leastbit = least_bit(leftbits);
-	  compute_bit2idx(leastbit, i);
-	  b = smallbin_at(gm, i);
-	  p = b->fd;
-	  assert(chunksize(p) == small_index2size(i));
-	  unlink_first_small_chunk(gm, b, p, i);
-	  rsize = small_index2size(i) - nb;
-	  /* Fit here cannot be remainderless if 4byte sizes */
-	  if (SIZE_T_SIZE != 4 && rsize < MIN_CHUNK_SIZE)
-	    set_inuse_and_pinuse(gm, p, small_index2size(i));
-	  else {
-	    set_size_and_pinuse_of_inuse_chunk(gm, p, nb);
-	    r = chunk_plus_offset(p, nb);
-	    set_size_and_pinuse_of_free_chunk(r, rsize);
-	    replace_dv(gm, r, rsize);
-	  }
-	  mem = chunk2mem(p);
-	  check_malloced_chunk(gm, mem, nb);
-	  goto postaction;
-	}
-
-	else if (gm->treemap != 0 && (mem = tmalloc_small(gm, nb)) != 0) {
-	  check_malloced_chunk(gm, mem, nb);
-	  goto postaction;
-	}
-      }
-    }
-    else if (bytes >= MAX_REQUEST)
-      nb = MAX_SIZE_T; /* Too big to allocate. Force failure (in sys alloc) */
-    else {
-      nb = pad_request(bytes);
-      if (gm->treemap != 0 && (mem = tmalloc_large(gm, nb)) != 0) {
-	check_malloced_chunk(gm, mem, nb);
-	goto postaction;
-      }
-    }
-
-    if (nb <= gm->dvsize) {
-      size_t rsize = gm->dvsize - nb;
-      mchunkptr p = gm->dv;
-      if (rsize >= MIN_CHUNK_SIZE) { /* split dv */
-	mchunkptr r = gm->dv = chunk_plus_offset(p, nb);
-	gm->dvsize = rsize;
-	set_size_and_pinuse_of_free_chunk(r, rsize);
-	set_size_and_pinuse_of_inuse_chunk(gm, p, nb);
-      }
-      else { /* exhaust dv */
-	size_t dvs = gm->dvsize;
-	gm->dvsize = 0;
-	gm->dv = 0;
-	set_inuse_and_pinuse(gm, p, dvs);
-      }
-      mem = chunk2mem(p);
-      check_malloced_chunk(gm, mem, nb);
-      goto postaction;
-    }
-
-    else if (nb < gm->topsize) { /* Split top */
-      size_t rsize = gm->topsize -= nb;
-      mchunkptr p = gm->top;
-      mchunkptr r = gm->top = chunk_plus_offset(p, nb);
-      r->head = rsize | PINUSE_BIT;
-      set_size_and_pinuse_of_inuse_chunk(gm, p, nb);
-      mem = chunk2mem(p);
-      check_top_chunk(gm, gm->top);
-      check_malloced_chunk(gm, mem, nb);
-      goto postaction;
-    }
-
-    mem = sys_alloc(gm, nb);
-
-  postaction:
-    POSTACTION(gm);
-    return mem;
-  }
-
-  return 0;
-}
-
-void dlfree(void* mem) {
-  /*
-     Consolidate freed chunks with preceding or succeeding bordering
-     free chunks, if they exist, and then place in a bin.  Intermixed
-     with special cases for top, dv, mmapped chunks, and usage errors.
-  */
-
-  if (mem != 0) {
-    mchunkptr p  = mem2chunk(mem);
-#if FOOTERS
-    mstate fm = get_mstate_for(p);
-    if (!ok_magic(fm)) {
-      USAGE_ERROR_ACTION(fm, p);
-      return;
-    }
-#else /* FOOTERS */
-#define fm gm
-#endif /* FOOTERS */
-    if (!PREACTION(fm)) {
-      check_inuse_chunk(fm, p);
-      if (RTCHECK(ok_address(fm, p) && ok_cinuse(p))) {
-	size_t psize = chunksize(p);
-	mchunkptr next = chunk_plus_offset(p, psize);
-	if (!pinuse(p)) {
-	  size_t prevsize = p->prev_foot;
-	  if ((prevsize & IS_MMAPPED_BIT) != 0) {
-	    prevsize &= ~IS_MMAPPED_BIT;
-	    psize += prevsize + MMAP_FOOT_PAD;
-	    if (CALL_MUNMAP((char*)p - prevsize, psize) == 0)
-	      fm->footprint -= psize;
-	    goto postaction;
-	  }
-	  else {
-	    mchunkptr prev = chunk_minus_offset(p, prevsize);
-	    psize += prevsize;
-	    p = prev;
-	    if (RTCHECK(ok_address(fm, prev))) { /* consolidate backward */
-	      if (p != fm->dv) {
-		unlink_chunk(fm, p, prevsize);
-	      }
-	      else if ((next->head & INUSE_BITS) == INUSE_BITS) {
-		fm->dvsize = psize;
-		set_free_with_pinuse(p, psize, next);
-		goto postaction;
-	      }
-	    }
-	    else
-	      goto erroraction;
-	  }
-	}
-
-	if (RTCHECK(ok_next(p, next) && ok_pinuse(next))) {
-	  if (!cinuse(next)) {  /* consolidate forward */
-	    if (next == fm->top) {
-	      size_t tsize = fm->topsize += psize;
-	      fm->top = p;
-	      p->head = tsize | PINUSE_BIT;
-	      if (p == fm->dv) {
-		fm->dv = 0;
-		fm->dvsize = 0;
-	      }
-	      if (should_trim(fm, tsize))
-		sys_trim(fm, 0);
-	      goto postaction;
-	    }
-	    else if (next == fm->dv) {
-	      size_t dsize = fm->dvsize += psize;
-	      fm->dv = p;
-	      set_size_and_pinuse_of_free_chunk(p, dsize);
-	      goto postaction;
-	    }
-	    else {
-	      size_t nsize = chunksize(next);
-	      psize += nsize;
-	      unlink_chunk(fm, next, nsize);
-	      set_size_and_pinuse_of_free_chunk(p, psize);
-	      if (p == fm->dv) {
-		fm->dvsize = psize;
-		goto postaction;
-	      }
-	    }
-	  }
-	  else
-	    set_free_with_pinuse(p, psize, next);
-
-	  if (is_small(psize)) {
-	    insert_small_chunk(fm, p, psize);
-	    check_free_chunk(fm, p);
-	  }
-	  else {
-	    tchunkptr tp = (tchunkptr)p;
-	    insert_large_chunk(fm, tp, psize);
-	    check_free_chunk(fm, p);
-	    if (--fm->release_checks == 0)
-	      release_unused_segments(fm);
-	  }
-	  goto postaction;
-	}
-      }
-    erroraction:
-      USAGE_ERROR_ACTION(fm, p);
-    postaction:
-      POSTACTION(fm);
-    }
-  }
-#if !FOOTERS
-#undef fm
-#endif /* FOOTERS */
-}
-
-void* dlcalloc(size_t n_elements, size_t elem_size) {
-  void* mem;
-  size_t req = 0;
-  if (n_elements != 0) {
-    req = n_elements * elem_size;
-    if (((n_elements | elem_size) & ~(size_t)0xffff) &&
-	(req / n_elements != elem_size))
-      req = MAX_SIZE_T; /* force downstream failure on overflow */
-  }
-  mem = dlmalloc(req);
-  if (mem != 0 && calloc_must_clear(mem2chunk(mem)))
-    memset(mem, 0, req);
-  return mem;
-}
-
-void* dlrealloc(void* oldmem, size_t bytes) {
-  if (oldmem == 0)
-    return dlmalloc(bytes);
-#ifdef REALLOC_ZERO_BYTES_FREES
-  if (bytes == 0) {
-    dlfree(oldmem);
-    return 0;
-  }
-#endif /* REALLOC_ZERO_BYTES_FREES */
-  else {
-#if ! FOOTERS
-    mstate m = gm;
-#else /* FOOTERS */
-    mstate m = get_mstate_for(mem2chunk(oldmem));
-    if (!ok_magic(m)) {
-      USAGE_ERROR_ACTION(m, oldmem);
-      return 0;
-    }
-#endif /* FOOTERS */
-    return internal_realloc(m, oldmem, bytes);
-  }
-}
-
-void* dlmemalign(size_t alignment, size_t bytes) {
-  return internal_memalign(gm, alignment, bytes);
-}
-
-void** dlindependent_calloc(size_t n_elements, size_t elem_size,
-				 void* chunks[]) {
-  size_t sz = elem_size; /* serves as 1-element array */
-  return ialloc(gm, n_elements, &sz, 3, chunks);
-}
-
-void** dlindependent_comalloc(size_t n_elements, size_t sizes[],
-				   void* chunks[]) {
-  return ialloc(gm, n_elements, sizes, 0, chunks);
-}
-
-void* dlvalloc(size_t bytes) {
-  size_t pagesz;
-  ensure_initialization();
-  pagesz = mparams.page_size;
-  return dlmemalign(pagesz, bytes);
-}
-
-void* dlpvalloc(size_t bytes) {
-  size_t pagesz;
-  ensure_initialization();
-  pagesz = mparams.page_size;
-  return dlmemalign(pagesz, (bytes + pagesz - SIZE_T_ONE) & ~(pagesz - SIZE_T_ONE));
-}
-
-int dlmalloc_trim(size_t pad) {
-  ensure_initialization();
-  int result = 0;
-  if (!PREACTION(gm)) {
-    result = sys_trim(gm, pad);
-    POSTACTION(gm);
-  }
-  return result;
-}
-
-size_t dlmalloc_footprint(void) {
-  return gm->footprint;
-}
-
-size_t dlmalloc_max_footprint(void) {
-  return gm->max_footprint;
-}
-
-#if !NO_MALLINFO
-struct mallinfo dlmallinfo(void) {
-  return internal_mallinfo(gm);
-}
-#endif /* NO_MALLINFO */
-
-void dlmalloc_stats() {
-  internal_malloc_stats(gm);
-}
-
-int dlmallopt(int param_number, int value) {
-  return change_mparam(param_number, value);
-}
-
-#endif /* !ONLY_MSPACES */
-
-size_t dlmalloc_usable_size(void* mem) {
-  if (mem != 0) {
-    mchunkptr p = mem2chunk(mem);
-    if (cinuse(p))
-      return chunksize(p) - overhead_for(p);
-  }
-  return 0;
-}
-
-/* ----------------------------- user mspaces ---------------------------- */
-
-#if MSPACES
-
-static mstate init_user_mstate(char* tbase, size_t tsize) {
-  size_t msize = pad_request(sizeof(struct malloc_state));
-  mchunkptr mn;
-  mchunkptr msp = align_as_chunk(tbase);
-  mstate m = (mstate)(chunk2mem(msp));
-  memset(m, 0, msize);
-  (void)INITIAL_LOCK(&m->mutex);
-  msp->head = (msize|PINUSE_BIT|CINUSE_BIT);
-  m->seg.base = m->least_addr = tbase;
-  m->seg.size = m->footprint = m->max_footprint = tsize;
-  m->magic = mparams.magic;
-  m->release_checks = MAX_RELEASE_CHECK_RATE;
-  m->mflags = mparams.default_mflags;
-  m->extp = 0;
-  m->exts = 0;
-  disable_contiguous(m);
-  init_bins(m);
-  mn = next_chunk(mem2chunk(m));
-  init_top(m, mn, (size_t)((tbase + tsize) - (char*)mn) - TOP_FOOT_SIZE);
-  check_top_chunk(m, m->top);
-  return m;
-}
-
-mspace create_mspace(size_t capacity, int locked) {
-  mstate m = 0;
-  size_t msize;
-  ensure_initialization();
-  msize = pad_request(sizeof(struct malloc_state));
-  if (capacity < (size_t) -(msize + TOP_FOOT_SIZE + mparams.page_size)) {
-    size_t rs = ((capacity == 0)? mparams.granularity :
-		 (capacity + TOP_FOOT_SIZE + msize));
-    size_t tsize = granularity_align(rs);
-    char* tbase = (char*)(CALL_MMAP(tsize));
-    if (tbase != CMFAIL) {
-      m = init_user_mstate(tbase, tsize);
-      m->seg.sflags = IS_MMAPPED_BIT;
-      set_lock(m, locked);
-    }
-  }
-  return (mspace)m;
-}
-
-mspace create_mspace_with_base(void* base, size_t capacity, int locked) {
-  mstate m = 0;
-  size_t msize;
-  ensure_initialization();
-  msize = pad_request(sizeof(struct malloc_state));
-  if (capacity > msize + TOP_FOOT_SIZE &&
-      capacity < (size_t) -(msize + TOP_FOOT_SIZE + mparams.page_size)) {
-    m = init_user_mstate((char*)base, capacity);
-    m->seg.sflags = EXTERN_BIT;
-    set_lock(m, locked);
-  }
-  return (mspace)m;
-}
-
-int mspace_mmap_large_chunks(mspace msp, int enable) {
-  int ret = 0;
-  mstate ms = (mstate)msp;
-  if (!PREACTION(ms)) {
-    if (use_mmap(ms))
-      ret = 1;
-    if (enable)
-      enable_mmap(ms);
-    else
-      disable_mmap(ms);
-    POSTACTION(ms);
-  }
-  return ret;
-}
-
-size_t destroy_mspace(mspace msp) {
-  size_t freed = 0;
-  mstate ms = (mstate)msp;
-  if (ok_magic(ms)) {
-    msegmentptr sp = &ms->seg;
-    while (sp != 0) {
-      char* base = sp->base;
-      size_t size = sp->size;
-      flag_t flag = sp->sflags;
-      sp = sp->next;
-      if ((flag & IS_MMAPPED_BIT) && !(flag & EXTERN_BIT) &&
-	  CALL_MUNMAP(base, size) == 0)
-	freed += size;
-    }
-  }
-  else {
-    USAGE_ERROR_ACTION(ms,ms);
-  }
-  return freed;
-}
-
-/*
-  mspace versions of routines are near-clones of the global
-  versions. This is not so nice but better than the alternatives.
-*/
-
-
-void* mspace_malloc(mspace msp, size_t bytes) {
-  mstate ms = (mstate)msp;
-  if (!ok_magic(ms)) {
-    USAGE_ERROR_ACTION(ms,ms);
-    return 0;
-  }
-  if (!PREACTION(ms)) {
-    void* mem;
-    size_t nb;
-    if (bytes <= MAX_SMALL_REQUEST) {
-      bindex_t idx;
-      binmap_t smallbits;
-      nb = (bytes < MIN_REQUEST)? MIN_CHUNK_SIZE : pad_request(bytes);
-      idx = small_index(nb);
-      smallbits = ms->smallmap >> idx;
-
-      if ((smallbits & 0x3U) != 0) { /* Remainderless fit to a smallbin. */
-	mchunkptr b, p;
-	idx += ~smallbits & 1;       /* Uses next bin if idx empty */
-	b = smallbin_at(ms, idx);
-	p = b->fd;
-	assert(chunksize(p) == small_index2size(idx));
-	unlink_first_small_chunk(ms, b, p, idx);
-	set_inuse_and_pinuse(ms, p, small_index2size(idx));
-	mem = chunk2mem(p);
-	check_malloced_chunk(ms, mem, nb);
-	goto postaction;
-      }
-
-      else if (nb > ms->dvsize) {
-	if (smallbits != 0) { /* Use chunk in next nonempty smallbin */
-	  mchunkptr b, p, r;
-	  size_t rsize;
-	  bindex_t i;
-	  binmap_t leftbits = (smallbits << idx) & left_bits(idx2bit(idx));
-	  binmap_t leastbit = least_bit(leftbits);
-	  compute_bit2idx(leastbit, i);
-	  b = smallbin_at(ms, i);
-	  p = b->fd;
-	  assert(chunksize(p) == small_index2size(i));
-	  unlink_first_small_chunk(ms, b, p, i);
-	  rsize = small_index2size(i) - nb;
-	  /* Fit here cannot be remainderless if 4byte sizes */
-	  if (SIZE_T_SIZE != 4 && rsize < MIN_CHUNK_SIZE)
-	    set_inuse_and_pinuse(ms, p, small_index2size(i));
-	  else {
-	    set_size_and_pinuse_of_inuse_chunk(ms, p, nb);
-	    r = chunk_plus_offset(p, nb);
-	    set_size_and_pinuse_of_free_chunk(r, rsize);
-	    replace_dv(ms, r, rsize);
-	  }
-	  mem = chunk2mem(p);
-	  check_malloced_chunk(ms, mem, nb);
-	  goto postaction;
-	}
-
-	else if (ms->treemap != 0 && (mem = tmalloc_small(ms, nb)) != 0) {
-	  check_malloced_chunk(ms, mem, nb);
-	  goto postaction;
-	}
-      }
-    }
-    else if (bytes >= MAX_REQUEST)
-      nb = MAX_SIZE_T; /* Too big to allocate. Force failure (in sys alloc) */
-    else {
-      nb = pad_request(bytes);
-      if (ms->treemap != 0 && (mem = tmalloc_large(ms, nb)) != 0) {
-	check_malloced_chunk(ms, mem, nb);
-	goto postaction;
-      }
-    }
-
-    if (nb <= ms->dvsize) {
-      size_t rsize = ms->dvsize - nb;
-      mchunkptr p = ms->dv;
-      if (rsize >= MIN_CHUNK_SIZE) { /* split dv */
-	mchunkptr r = ms->dv = chunk_plus_offset(p, nb);
-	ms->dvsize = rsize;
-	set_size_and_pinuse_of_free_chunk(r, rsize);
-	set_size_and_pinuse_of_inuse_chunk(ms, p, nb);
-      }
-      else { /* exhaust dv */
-	size_t dvs = ms->dvsize;
-	ms->dvsize = 0;
-	ms->dv = 0;
-	set_inuse_and_pinuse(ms, p, dvs);
-      }
-      mem = chunk2mem(p);
-      check_malloced_chunk(ms, mem, nb);
-      goto postaction;
-    }
-
-    else if (nb < ms->topsize) { /* Split top */
-      size_t rsize = ms->topsize -= nb;
-      mchunkptr p = ms->top;
-      mchunkptr r = ms->top = chunk_plus_offset(p, nb);
-      r->head = rsize | PINUSE_BIT;
-      set_size_and_pinuse_of_inuse_chunk(ms, p, nb);
-      mem = chunk2mem(p);
-      check_top_chunk(ms, ms->top);
-      check_malloced_chunk(ms, mem, nb);
-      goto postaction;
-    }
-
-    mem = sys_alloc(ms, nb);
-
-  postaction:
-    POSTACTION(ms);
-    return mem;
-  }
-
-  return 0;
-}
-
-void mspace_free(mspace msp, void* mem) {
-  if (mem != 0) {
-    mchunkptr p  = mem2chunk(mem);
-#if FOOTERS
-    mstate fm = get_mstate_for(p);
-#else /* FOOTERS */
-    mstate fm = (mstate)msp;
-#endif /* FOOTERS */
-    if (!ok_magic(fm)) {
-      USAGE_ERROR_ACTION(fm, p);
-      return;
-    }
-    if (!PREACTION(fm)) {
-      check_inuse_chunk(fm, p);
-      if (RTCHECK(ok_address(fm, p) && ok_cinuse(p))) {
-	size_t psize = chunksize(p);
-	mchunkptr next = chunk_plus_offset(p, psize);
-	if (!pinuse(p)) {
-	  size_t prevsize = p->prev_foot;
-	  if ((prevsize & IS_MMAPPED_BIT) != 0) {
-	    prevsize &= ~IS_MMAPPED_BIT;
-	    psize += prevsize + MMAP_FOOT_PAD;
-	    if (CALL_MUNMAP((char*)p - prevsize, psize) == 0)
-	      fm->footprint -= psize;
-	    goto postaction;
-	  }
-	  else {
-	    mchunkptr prev = chunk_minus_offset(p, prevsize);
-	    psize += prevsize;
-	    p = prev;
-	    if (RTCHECK(ok_address(fm, prev))) { /* consolidate backward */
-	      if (p != fm->dv) {
-		unlink_chunk(fm, p, prevsize);
-	      }
-	      else if ((next->head & INUSE_BITS) == INUSE_BITS) {
-		fm->dvsize = psize;
-		set_free_with_pinuse(p, psize, next);
-		goto postaction;
-	      }
-	    }
-	    else
-	      goto erroraction;
-	  }
-	}
-
-	if (RTCHECK(ok_next(p, next) && ok_pinuse(next))) {
-	  if (!cinuse(next)) {  /* consolidate forward */
-	    if (next == fm->top) {
-	      size_t tsize = fm->topsize += psize;
-	      fm->top = p;
-	      p->head = tsize | PINUSE_BIT;
-	      if (p == fm->dv) {
-		fm->dv = 0;
-		fm->dvsize = 0;
-	      }
-	      if (should_trim(fm, tsize))
-		sys_trim(fm, 0);
-	      goto postaction;
-	    }
-	    else if (next == fm->dv) {
-	      size_t dsize = fm->dvsize += psize;
-	      fm->dv = p;
-	      set_size_and_pinuse_of_free_chunk(p, dsize);
-	      goto postaction;
-	    }
-	    else {
-	      size_t nsize = chunksize(next);
-	      psize += nsize;
-	      unlink_chunk(fm, next, nsize);
-	      set_size_and_pinuse_of_free_chunk(p, psize);
-	      if (p == fm->dv) {
-		fm->dvsize = psize;
-		goto postaction;
-	      }
-	    }
-	  }
-	  else
-	    set_free_with_pinuse(p, psize, next);
-
-	  if (is_small(psize)) {
-	    insert_small_chunk(fm, p, psize);
-	    check_free_chunk(fm, p);
-	  }
-	  else {
-	    tchunkptr tp = (tchunkptr)p;
-	    insert_large_chunk(fm, tp, psize);
-	    check_free_chunk(fm, p);
-	    if (--fm->release_checks == 0)
-	      release_unused_segments(fm);
-	  }
-	  goto postaction;
-	}
-      }
-    erroraction:
-      USAGE_ERROR_ACTION(fm, p);
-    postaction:
-      POSTACTION(fm);
-    }
-  }
-}
-
-void* mspace_calloc(mspace msp, size_t n_elements, size_t elem_size) {
-  void* mem;
-  size_t req = 0;
-  mstate ms = (mstate)msp;
-  if (!ok_magic(ms)) {
-    USAGE_ERROR_ACTION(ms,ms);
-    return 0;
-  }
-  if (n_elements != 0) {
-    req = n_elements * elem_size;
-    if (((n_elements | elem_size) & ~(size_t)0xffff) &&
-	(req / n_elements != elem_size))
-      req = MAX_SIZE_T; /* force downstream failure on overflow */
-  }
-  mem = internal_malloc(ms, req);
-  if (mem != 0 && calloc_must_clear(mem2chunk(mem)))
-    memset(mem, 0, req);
-  return mem;
-}
-
-void* mspace_realloc(mspace msp, void* oldmem, size_t bytes) {
-  if (oldmem == 0)
-    return mspace_malloc(msp, bytes);
-#ifdef REALLOC_ZERO_BYTES_FREES
-  if (bytes == 0) {
-    mspace_free(msp, oldmem);
-    return 0;
-  }
-#endif /* REALLOC_ZERO_BYTES_FREES */
-  else {
-#if FOOTERS
-    mchunkptr p  = mem2chunk(oldmem);
-    mstate ms = get_mstate_for(p);
-#else /* FOOTERS */
-    mstate ms = (mstate)msp;
-#endif /* FOOTERS */
-    if (!ok_magic(ms)) {
-      USAGE_ERROR_ACTION(ms,ms);
-      return 0;
-    }
-    return internal_realloc(ms, oldmem, bytes);
-  }
-}
-
-void* mspace_memalign(mspace msp, size_t alignment, size_t bytes) {
-  mstate ms = (mstate)msp;
-  if (!ok_magic(ms)) {
-    USAGE_ERROR_ACTION(ms,ms);
-    return 0;
-  }
-  return internal_memalign(ms, alignment, bytes);
-}
-
-void** mspace_independent_calloc(mspace msp, size_t n_elements,
-				 size_t elem_size, void* chunks[]) {
-  size_t sz = elem_size; /* serves as 1-element array */
-  mstate ms = (mstate)msp;
-  if (!ok_magic(ms)) {
-    USAGE_ERROR_ACTION(ms,ms);
-    return 0;
-  }
-  return ialloc(ms, n_elements, &sz, 3, chunks);
-}
-
-void** mspace_independent_comalloc(mspace msp, size_t n_elements,
-				   size_t sizes[], void* chunks[]) {
-  mstate ms = (mstate)msp;
-  if (!ok_magic(ms)) {
-    USAGE_ERROR_ACTION(ms,ms);
-    return 0;
-  }
-  return ialloc(ms, n_elements, sizes, 0, chunks);
-}
-
-int mspace_trim(mspace msp, size_t pad) {
-  int result = 0;
-  mstate ms = (mstate)msp;
-  if (ok_magic(ms)) {
-    if (!PREACTION(ms)) {
-      result = sys_trim(ms, pad);
-      POSTACTION(ms);
-    }
-  }
-  else {
-    USAGE_ERROR_ACTION(ms,ms);
-  }
-  return result;
-}
-
-void mspace_malloc_stats(mspace msp) {
-  mstate ms = (mstate)msp;
-  if (ok_magic(ms)) {
-    internal_malloc_stats(ms);
-  }
-  else {
-    USAGE_ERROR_ACTION(ms,ms);
-  }
-}
-
-size_t mspace_footprint(mspace msp) {
-  size_t result = 0;
-  mstate ms = (mstate)msp;
-  if (ok_magic(ms)) {
-    result = ms->footprint;
-  }
-  else {
-    USAGE_ERROR_ACTION(ms,ms);
-  }
-  return result;
-}
-
-
-size_t mspace_max_footprint(mspace msp) {
-  size_t result = 0;
-  mstate ms = (mstate)msp;
-  if (ok_magic(ms)) {
-    result = ms->max_footprint;
-  }
-  else {
-    USAGE_ERROR_ACTION(ms,ms);
-  }
-  return result;
-}
-
-
-#if !NO_MALLINFO
-struct mallinfo mspace_mallinfo(mspace msp) {
-  mstate ms = (mstate)msp;
-  if (!ok_magic(ms)) {
-    USAGE_ERROR_ACTION(ms,ms);
-  }
-  return internal_mallinfo(ms);
-}
-#endif /* NO_MALLINFO */
-
-size_t mspace_usable_size(void* mem) {
-  if (mem != 0) {
-    mchunkptr p = mem2chunk(mem);
-    if (cinuse(p))
-      return chunksize(p) - overhead_for(p);
-  }
-  return 0;
-}
-
-int mspace_mallopt(int param_number, int value) {
-  return change_mparam(param_number, value);
-}
-
-#endif /* MSPACES */
-
-/* -------------------- Alternative MORECORE functions ------------------- */
-
-/*
-  Guidelines for creating a custom version of MORECORE:
-
-  * For best performance, MORECORE should allocate in multiples of pagesize.
-  * MORECORE may allocate more memory than requested. (Or even less,
-      but this will usually result in a malloc failure.)
-  * MORECORE must not allocate memory when given argument zero, but
-      instead return one past the end address of memory from previous
-      nonzero call.
-  * For best performance, consecutive calls to MORECORE with positive
-      arguments should return increasing addresses, indicating that
-      space has been contiguously extended.
-  * Even though consecutive calls to MORECORE need not return contiguous
-      addresses, it must be OK for malloc'ed chunks to span multiple
-      regions in those cases where they do happen to be contiguous.
-  * MORECORE need not handle negative arguments -- it may instead
-      just return MFAIL when given negative arguments.
-      Negative arguments are always multiples of pagesize. MORECORE
-      must not misinterpret negative args as large positive unsigned
-      args. You can suppress all such calls from even occurring by defining
-      MORECORE_CANNOT_TRIM,
-
-  As an example alternative MORECORE, here is a custom allocator
-  kindly contributed for pre-OSX macOS.  It uses virtually but not
-  necessarily physically contiguous non-paged memory (locked in,
-  present and won't get swapped out).  You can use it by uncommenting
-  this section, adding some #includes, and setting up the appropriate
-  defines above:
-
-      #define MORECORE osMoreCore
-
-  There is also a shutdown routine that should somehow be called for
-  cleanup upon program exit.
-
-  #define MAX_POOL_ENTRIES 100
-  #define MINIMUM_MORECORE_SIZE  (64 * 1024U)
-  static int next_os_pool;
-  void *our_os_pools[MAX_POOL_ENTRIES];
-
-  void *osMoreCore(int size)
-  {
-    void *ptr = 0;
-    static void *sbrk_top = 0;
-
-    if (size > 0)
-    {
-      if (size < MINIMUM_MORECORE_SIZE)
-	 size = MINIMUM_MORECORE_SIZE;
-      if (CurrentExecutionLevel() == kTaskLevel)
-	 ptr = PoolAllocateResident(size + RM_PAGE_SIZE, 0);
-      if (ptr == 0)
-      {
-	return (void *) MFAIL;
-      }
-      // save ptrs so they can be freed during cleanup
-      our_os_pools[next_os_pool] = ptr;
-      next_os_pool++;
-      ptr = (void *) ((((size_t) ptr) + RM_PAGE_MASK) & ~RM_PAGE_MASK);
-      sbrk_top = (char *) ptr + size;
-      return ptr;
-    }
-    else if (size < 0)
-    {
-      // we don't currently support shrink behavior
-      return (void *) MFAIL;
-    }
-    else
-    {
-      return sbrk_top;
-    }
-  }
-
-  // cleanup any allocated memory pools
-  // called as last thing before shutting down driver
-
-  void osCleanupMem(void)
-  {
-    void **ptr;
-
-    for (ptr = our_os_pools; ptr < &our_os_pools[MAX_POOL_ENTRIES]; ptr++)
-      if (*ptr)
-      {
-	 PoolDeallocate(*ptr);
-	 *ptr = 0;
-      }
-  }
-
-*/
-
-
-/* -----------------------------------------------------------------------
-History:
-    V2.8.4 (not yet released)
-      * Add mspace_mmap_large_chunks; thanks to Jean Brouwers
-      * Fix insufficient sys_alloc padding when using 16byte alignment
-      * Fix bad error check in mspace_footprint
-      * Adaptations for ptmalloc, courtesy of Wolfram Gloger.
-      * Reentrant spin locks, courtesy of Earl Chew and others
-      * Win32 improvements, courtesy of Niall Douglas and Earl Chew
-      * Add NO_SEGMENT_TRAVERSAL and MAX_RELEASE_CHECK_RATE options
-      * Extension hook in malloc_state
-      * Various small adjustments to reduce warnings on some compilers
-      * Various configuration extensions/changes for more platforms. Thanks
-	 to all who contributed these.
-
-    V2.8.3 Thu Sep 22 11:16:32 2005  Doug Lea  (dl at gee)
-      * Add max_footprint functions
-      * Ensure all appropriate literals are size_t
-      * Fix conditional compilation problem for some #define settings
-      * Avoid concatenating segments with the one provided
-	in create_mspace_with_base
-      * Rename some variables to avoid compiler shadowing warnings
-      * Use explicit lock initialization.
-      * Better handling of sbrk interference.
-      * Simplify and fix segment insertion, trimming and mspace_destroy
-      * Reinstate REALLOC_ZERO_BYTES_FREES option from 2.7.x
-      * Thanks especially to Dennis Flanagan for help on these.
-
-    V2.8.2 Sun Jun 12 16:01:10 2005  Doug Lea  (dl at gee)
-      * Fix memalign brace error.
-
-    V2.8.1 Wed Jun  8 16:11:46 2005  Doug Lea  (dl at gee)
-      * Fix improper #endif nesting in C++
-      * Add explicit casts needed for C++
-
-    V2.8.0 Mon May 30 14:09:02 2005  Doug Lea  (dl at gee)
-      * Use trees for large bins
-      * Support mspaces
-      * Use segments to unify sbrk-based and mmap-based system allocation,
-	removing need for emulation on most platforms without sbrk.
-      * Default safety checks
-      * Optional footer checks. Thanks to William Robertson for the idea.
-      * Internal code refactoring
-      * Incorporate suggestions and platform-specific changes.
-	Thanks to Dennis Flanagan, Colin Plumb, Niall Douglas,
-	Aaron Bachmann,  Emery Berger, and others.
-      * Speed up non-fastbin processing enough to remove fastbins.
-      * Remove useless cfree() to avoid conflicts with other apps.
-      * Remove internal memcpy, memset. Compilers handle builtins better.
-      * Remove some options that no one ever used and rename others.
-
-    V2.7.2 Sat Aug 17 09:07:30 2002  Doug Lea  (dl at gee)
-      * Fix malloc_state bitmap array misdeclaration
-
-    V2.7.1 Thu Jul 25 10:58:03 2002  Doug Lea  (dl at gee)
-      * Allow tuning of FIRST_SORTED_BIN_SIZE
-      * Use PTR_UINT as type for all ptr->int casts. Thanks to John Belmonte.
-      * Better detection and support for non-contiguousness of MORECORE.
-	Thanks to Andreas Mueller, Conal Walsh, and Wolfram Gloger
-      * Bypass most of malloc if no frees. Thanks To Emery Berger.
-      * Fix freeing of old top non-contiguous chunk im sysmalloc.
-      * Raised default trim and map thresholds to 256K.
-      * Fix mmap-related #defines. Thanks to Lubos Lunak.
-      * Fix copy macros; added LACKS_FCNTL_H. Thanks to Neal Walfield.
-      * Branch-free bin calculation
-      * Default trim and mmap thresholds now 256K.
-
-    V2.7.0 Sun Mar 11 14:14:06 2001  Doug Lea  (dl at gee)
-      * Introduce independent_comalloc and independent_calloc.
-	Thanks to Michael Pachos for motivation and help.
-      * Make optional .h file available
-      * Allow > 2GB requests on 32bit systems.
-      * new WIN32 sbrk, mmap, munmap, lock code from <Walter@GeNeSys-e.de>.
-	Thanks also to Andreas Mueller <a.mueller at paradatec.de>,
-	and Anonymous.
-      * Allow override of MALLOC_ALIGNMENT (Thanks to Ruud Waij for
-	helping test this.)
-      * memalign: check alignment arg
-      * realloc: don't try to shift chunks backwards, since this
-	leads to  more fragmentation in some programs and doesn't
-	seem to help in any others.
-      * Collect all cases in malloc requiring system memory into sysmalloc
-      * Use mmap as backup to sbrk
-      * Place all internal state in malloc_state
-      * Introduce fastbins (although similar to 2.5.1)
-      * Many minor tunings and cosmetic improvements
-      * Introduce USE_PUBLIC_MALLOC_WRAPPERS, USE_MALLOC_LOCK
-      * Introduce MALLOC_FAILURE_ACTION, MORECORE_CONTIGUOUS
-	Thanks to Tony E. Bennett <tbennett@nvidia.com> and others.
-      * Include errno.h to support default failure action.
-
-    V2.6.6 Sun Dec  5 07:42:19 1999  Doug Lea  (dl at gee)
-      * return null for negative arguments
-      * Added Several WIN32 cleanups from Martin C. Fong <mcfong at yahoo.com>
-	 * Add 'LACKS_SYS_PARAM_H' for those systems without 'sys/param.h'
-	  (e.g. WIN32 platforms)
-	 * Cleanup header file inclusion for WIN32 platforms
-	 * Cleanup code to avoid Microsoft Visual C++ compiler complaints
-	 * Add 'USE_DL_PREFIX' to quickly allow co-existence with existing
-	   memory allocation routines
-	 * Set 'malloc_getpagesize' for WIN32 platforms (needs more work)
-	 * Use 'assert' rather than 'ASSERT' in WIN32 code to conform to
-	   usage of 'assert' in non-WIN32 code
-	 * Improve WIN32 'sbrk()' emulation's 'findRegion()' routine to
-	   avoid infinite loop
-      * Always call 'fREe()' rather than 'free()'
-
-    V2.6.5 Wed Jun 17 15:57:31 1998  Doug Lea  (dl at gee)
-      * Fixed ordering problem with boundary-stamping
-
-    V2.6.3 Sun May 19 08:17:58 1996  Doug Lea  (dl at gee)
-      * Added pvalloc, as recommended by H.J. Liu
-      * Added 64bit pointer support mainly from Wolfram Gloger
-      * Added anonymously donated WIN32 sbrk emulation
-      * Malloc, calloc, getpagesize: add optimizations from Raymond Nijssen
-      * malloc_extend_top: fix mask error that caused wastage after
-	foreign sbrks
-      * Add linux mremap support code from HJ Liu
-
-    V2.6.2 Tue Dec  5 06:52:55 1995  Doug Lea  (dl at gee)
-      * Integrated most documentation with the code.
-      * Add support for mmap, with help from
-	Wolfram Gloger (Gloger@lrz.uni-muenchen.de).
-      * Use last_remainder in more cases.
-      * Pack bins using idea from  colin@nyx10.cs.du.edu
-      * Use ordered bins instead of best-fit threshold
-      * Eliminate block-local decls to simplify tracing and debugging.
-      * Support another case of realloc via move into top
-      * Fix error occurring when initial sbrk_base not word-aligned.
-      * Rely on page size for units instead of SBRK_UNIT to
-	avoid surprises about sbrk alignment conventions.
-      * Add mallinfo, mallopt. Thanks to Raymond Nijssen
-	(raymond@es.ele.tue.nl) for the suggestion.
-      * Add `pad' argument to malloc_trim and top_pad mallopt parameter.
-      * More precautions for cases where other routines call sbrk,
-	courtesy of Wolfram Gloger (Gloger@lrz.uni-muenchen.de).
-      * Added macros etc., allowing use in linux libc from
-	H.J. Lu (hjl@gnu.ai.mit.edu)
-      * Inverted this history list
-
-    V2.6.1 Sat Dec  2 14:10:57 1995  Doug Lea  (dl at gee)
-      * Re-tuned and fixed to behave more nicely with V2.6.0 changes.
-      * Removed all preallocation code since under current scheme
-	the work required to undo bad preallocations exceeds
-	the work saved in good cases for most test programs.
-      * No longer use return list or unconsolidated bins since
-	no scheme using them consistently outperforms those that don't
-	given above changes.
-      * Use best fit for very large chunks to prevent some worst-cases.
-      * Added some support for debugging
-
-    V2.6.0 Sat Nov  4 07:05:23 1995  Doug Lea  (dl at gee)
-      * Removed footers when chunks are in use. Thanks to
-	Paul Wilson (wilson@cs.texas.edu) for the suggestion.
-
-    V2.5.4 Wed Nov  1 07:54:51 1995  Doug Lea  (dl at gee)
-      * Added malloc_trim, with help from Wolfram Gloger
-	(wmglo@Dent.MED.Uni-Muenchen.DE).
-
-    V2.5.3 Tue Apr 26 10:16:01 1994  Doug Lea  (dl at g)
-
-    V2.5.2 Tue Apr  5 16:20:40 1994  Doug Lea  (dl at g)
-      * realloc: try to expand in both directions
-      * malloc: swap order of clean-bin strategy;
-      * realloc: only conditionally expand backwards
-      * Try not to scavenge used bins
-      * Use bin counts as a guide to preallocation
-      * Occasionally bin return list chunks in first scan
-      * Add a few optimizations from colin@nyx10.cs.du.edu
-
-    V2.5.1 Sat Aug 14 15:40:43 1993  Doug Lea  (dl at g)
-      * faster bin computation & slightly different binning
-      * merged all consolidations to one part of malloc proper
-	 (eliminating old malloc_find_space & malloc_clean_bin)
-      * Scan 2 returns chunks (not just 1)
-      * Propagate failure in realloc if malloc returns 0
-      * Add stuff to allow compilation on non-ANSI compilers
-	  from kpv@research.att.com
-
-    V2.5 Sat Aug  7 07:41:59 1993  Doug Lea  (dl at g.oswego.edu)
-      * removed potential for odd address access in prev_chunk
-      * removed dependency on getpagesize.h
-      * misc cosmetics and a bit more internal documentation
-      * anticosmetics: mangled names in macros to evade debugger strangeness
-      * tested on sparc, hp-700, dec-mips, rs6000
-	  with gcc & native cc (hp, dec only) allowing
-	  Detlefs & Zorn comparison study (in SIGPLAN Notices.)
-
-    Trial version Fri Aug 28 13:14:29 1992  Doug Lea  (dl at g.oswego.edu)
-      * Based loosely on libg++-1.2X malloc. (It retains some of the overall
-	 structure of old version,  but most details differ.)
-
-*/
diff --git a/compat/nedmalloc/nedmalloc.c b/compat/nedmalloc/nedmalloc.c
deleted file mode 100644
index 145255d..0000000
--- a/compat/nedmalloc/nedmalloc.c
+++ /dev/null
@@ -1,954 +0,0 @@
-/* Alternative malloc implementation for multiple threads without
-lock contention based on dlmalloc. (C) 2005-2006 Niall Douglas
-
-Boost Software License - Version 1.0 - August 17th, 2003
-
-Permission is hereby granted, free of charge, to any person or organization
-obtaining a copy of the software and accompanying documentation covered by
-this license (the "Software") to use, reproduce, display, distribute,
-execute, and transmit the Software, and to prepare derivative works of the
-Software, and to permit third-parties to whom the Software is furnished to
-do so, all subject to the following:
-
-The copyright notices in the Software and this entire statement, including
-the above license grant, this restriction and the following disclaimer,
-must be included in all copies of the Software, in whole or in part, and
-all derivative works of the Software, unless such copies or derivative
-works are solely in the form of machine-executable object code generated by
-a source language processor.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
-SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
-FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
-ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
-DEALINGS IN THE SOFTWARE.
-*/
-
-#ifdef _MSC_VER
-/* Enable full aliasing on MSVC */
-/*#pragma optimize("a", on)*/
-#endif
-
-#pragma GCC diagnostic ignored "-Wunused-parameter"
-
-/*#define FULLSANITYCHECKS*/
-
-#include "nedmalloc.h"
-#if defined(WIN32)
- #include <malloc.h>
-#endif
-#define MSPACES 1
-#define ONLY_MSPACES 1
-#ifndef USE_LOCKS
- #define USE_LOCKS 1
-#endif
-#define FOOTERS 1           /* Need to enable footers so frees lock the right mspace */
-#undef DEBUG				/* dlmalloc wants DEBUG either 0 or 1 */
-#ifdef _DEBUG
- #define DEBUG 1
-#else
- #define DEBUG 0
-#endif
-#ifdef NDEBUG               /* Disable assert checking on release builds */
- #undef DEBUG
-#endif
-/* The default of 64Kb means we spend too much time kernel-side */
-#ifndef DEFAULT_GRANULARITY
-#define DEFAULT_GRANULARITY (1*1024*1024)
-#endif
-/*#define USE_SPIN_LOCKS 0*/
-
-
-/*#define FORCEINLINE*/
-#include "malloc.c.h"
-#ifdef NDEBUG               /* Disable assert checking on release builds */
- #undef DEBUG
-#endif
-
-/* The maximum concurrent threads in a pool possible */
-#ifndef MAXTHREADSINPOOL
-#define MAXTHREADSINPOOL 16
-#endif
-/* The maximum number of threadcaches which can be allocated */
-#ifndef THREADCACHEMAXCACHES
-#define THREADCACHEMAXCACHES 256
-#endif
-/* The maximum size to be allocated from the thread cache */
-#ifndef THREADCACHEMAX
-#define THREADCACHEMAX 8192
-#endif
-#if 0
-/* The number of cache entries for finer grained bins. This is (topbitpos(THREADCACHEMAX)-4)*2 */
-#define THREADCACHEMAXBINS ((13-4)*2)
-#else
-/* The number of cache entries. This is (topbitpos(THREADCACHEMAX)-4) */
-#define THREADCACHEMAXBINS (13-4)
-#endif
-/* Point at which the free space in a thread cache is garbage collected */
-#ifndef THREADCACHEMAXFREESPACE
-#define THREADCACHEMAXFREESPACE (512*1024)
-#endif
-
-
-#ifdef WIN32
- #define TLSVAR			DWORD
- #define TLSALLOC(k)	(*(k)=TlsAlloc(), TLS_OUT_OF_INDEXES==*(k))
- #define TLSFREE(k)		(!TlsFree(k))
- #define TLSGET(k)		TlsGetValue(k)
- #define TLSSET(k, a)	(!TlsSetValue(k, a))
- #ifdef DEBUG
-static LPVOID ChkedTlsGetValue(DWORD idx)
-{
-	LPVOID ret=TlsGetValue(idx);
-	assert(S_OK==GetLastError());
-	return ret;
-}
-  #undef TLSGET
-  #define TLSGET(k) ChkedTlsGetValue(k)
- #endif
-#else
- #define TLSVAR			pthread_key_t
- #define TLSALLOC(k)	pthread_key_create(k, 0)
- #define TLSFREE(k)		pthread_key_delete(k)
- #define TLSGET(k)		pthread_getspecific(k)
- #define TLSSET(k, a)	pthread_setspecific(k, a)
-#endif
-
-#if 0
-/* Only enable if testing with valgrind. Causes misoperation */
-#define mspace_malloc(p, s) malloc(s)
-#define mspace_realloc(p, m, s) realloc(m, s)
-#define mspace_calloc(p, n, s) calloc(n, s)
-#define mspace_free(p, m) free(m)
-#endif
-
-
-#if defined(__cplusplus)
-#if !defined(NO_NED_NAMESPACE)
-namespace nedalloc {
-#else
-extern "C" {
-#endif
-#endif
-
-size_t nedblksize(void *mem) THROWSPEC
-{
-#if 0
-	/* Only enable if testing with valgrind. Causes misoperation */
-	return THREADCACHEMAX;
-#else
-	if(mem)
-	{
-		mchunkptr p=mem2chunk(mem);
-		assert(cinuse(p));	/* If this fails, someone tried to free a block twice */
-		if(cinuse(p))
-			return chunksize(p)-overhead_for(p);
-	}
-	return 0;
-#endif
-}
-
-void nedsetvalue(void *v) THROWSPEC					{ nedpsetvalue(0, v); }
-void * nedmalloc(size_t size) THROWSPEC				{ return nedpmalloc(0, size); }
-void * nedcalloc(size_t no, size_t size) THROWSPEC	{ return nedpcalloc(0, no, size); }
-void * nedrealloc(void *mem, size_t size) THROWSPEC	{ return nedprealloc(0, mem, size); }
-void   nedfree(void *mem) THROWSPEC					{ nedpfree(0, mem); }
-void * nedmemalign(size_t alignment, size_t bytes) THROWSPEC { return nedpmemalign(0, alignment, bytes); }
-#if !NO_MALLINFO
-struct mallinfo nedmallinfo(void) THROWSPEC			{ return nedpmallinfo(0); }
-#endif
-int    nedmallopt(int parno, int value) THROWSPEC	{ return nedpmallopt(0, parno, value); }
-int    nedmalloc_trim(size_t pad) THROWSPEC			{ return nedpmalloc_trim(0, pad); }
-void   nedmalloc_stats(void) THROWSPEC					{ nedpmalloc_stats(0); }
-size_t nedmalloc_footprint(void) THROWSPEC				{ return nedpmalloc_footprint(0); }
-void **nedindependent_calloc(size_t elemsno, size_t elemsize, void **chunks) THROWSPEC	{ return nedpindependent_calloc(0, elemsno, elemsize, chunks); }
-void **nedindependent_comalloc(size_t elems, size_t *sizes, void **chunks) THROWSPEC	{ return nedpindependent_comalloc(0, elems, sizes, chunks); }
-
-struct threadcacheblk_t;
-typedef struct threadcacheblk_t threadcacheblk;
-struct threadcacheblk_t
-{	/* Keep less than 16 bytes on 32 bit systems and 32 bytes on 64 bit systems */
-#ifdef FULLSANITYCHECKS
-	unsigned int magic;
-#endif
-	unsigned int lastUsed, size;
-	threadcacheblk *next, *prev;
-};
-typedef struct threadcache_t
-{
-#ifdef FULLSANITYCHECKS
-	unsigned int magic1;
-#endif
-	int mymspace;						/* Last mspace entry this thread used */
-	long threadid;
-	unsigned int mallocs, frees, successes;
-	size_t freeInCache;					/* How much free space is stored in this cache */
-	threadcacheblk *bins[(THREADCACHEMAXBINS+1)*2];
-#ifdef FULLSANITYCHECKS
-	unsigned int magic2;
-#endif
-} threadcache;
-struct nedpool_t
-{
-	MLOCK_T mutex;
-	void *uservalue;
-	int threads;						/* Max entries in m to use */
-	threadcache *caches[THREADCACHEMAXCACHES];
-	TLSVAR mycache;						/* Thread cache for this thread. 0 for unset, negative for use mspace-1 directly, otherwise is cache-1 */
-	mstate m[MAXTHREADSINPOOL+1];		/* mspace entries for this pool */
-};
-static nedpool syspool;
-
-static FORCEINLINE unsigned int size2binidx(size_t _size) THROWSPEC
-{	/* 8=1000	16=10000	20=10100	24=11000	32=100000	48=110000	4096=1000000000000 */
-	unsigned int topbit, size=(unsigned int)(_size>>4);
-	/* 16=1		20=1	24=1	32=10	48=11	64=100	96=110	128=1000	4096=100000000 */
-
-#if defined(__GNUC__)
-	topbit = sizeof(size)*__CHAR_BIT__ - 1 - __builtin_clz(size);
-#elif defined(_MSC_VER) && _MSC_VER>=1300
-	{
-	    unsigned long bsrTopBit;
-
-	    _BitScanReverse(&bsrTopBit, size);
-
-	    topbit = bsrTopBit;
-	}
-#else
-#if 0
-	union {
-		unsigned asInt[2];
-		double asDouble;
-	};
-	int n;
-
-	asDouble = (double)size + 0.5;
-	topbit = (asInt[!FOX_BIGENDIAN] >> 20) - 1023;
-#else
-	{
-		unsigned int x=size;
-		x = x | (x >> 1);
-		x = x | (x >> 2);
-		x = x | (x >> 4);
-		x = x | (x >> 8);
-		x = x | (x >>16);
-		x = ~x;
-		x = x - ((x >> 1) & 0x55555555);
-		x = (x & 0x33333333) + ((x >> 2) & 0x33333333);
-		x = (x + (x >> 4)) & 0x0F0F0F0F;
-		x = x + (x << 8);
-		x = x + (x << 16);
-		topbit=31 - (x >> 24);
-	}
-#endif
-#endif
-	return topbit;
-}
-
-
-#ifdef FULLSANITYCHECKS
-static void tcsanitycheck(threadcacheblk **ptr) THROWSPEC
-{
-	assert((ptr[0] && ptr[1]) || (!ptr[0] && !ptr[1]));
-	if(ptr[0] && ptr[1])
-	{
-		assert(nedblksize(ptr[0])>=sizeof(threadcacheblk));
-		assert(nedblksize(ptr[1])>=sizeof(threadcacheblk));
-		assert(*(unsigned int *) "NEDN"==ptr[0]->magic);
-		assert(*(unsigned int *) "NEDN"==ptr[1]->magic);
-		assert(!ptr[0]->prev);
-		assert(!ptr[1]->next);
-		if(ptr[0]==ptr[1])
-		{
-			assert(!ptr[0]->next);
-			assert(!ptr[1]->prev);
-		}
-	}
-}
-static void tcfullsanitycheck(threadcache *tc) THROWSPEC
-{
-	threadcacheblk **tcbptr=tc->bins;
-	int n;
-	for(n=0; n<=THREADCACHEMAXBINS; n++, tcbptr+=2)
-	{
-		threadcacheblk *b, *ob=0;
-		tcsanitycheck(tcbptr);
-		for(b=tcbptr[0]; b; ob=b, b=b->next)
-		{
-			assert(*(unsigned int *) "NEDN"==b->magic);
-			assert(!ob || ob->next==b);
-			assert(!ob || b->prev==ob);
-		}
-	}
-}
-#endif
-
-static NOINLINE void RemoveCacheEntries(nedpool *p, threadcache *tc, unsigned int age) THROWSPEC
-{
-#ifdef FULLSANITYCHECKS
-	tcfullsanitycheck(tc);
-#endif
-	if(tc->freeInCache)
-	{
-		threadcacheblk **tcbptr=tc->bins;
-		int n;
-		for(n=0; n<=THREADCACHEMAXBINS; n++, tcbptr+=2)
-		{
-			threadcacheblk **tcb=tcbptr+1;		/* come from oldest end of list */
-			/*tcsanitycheck(tcbptr);*/
-			for(; *tcb && tc->frees-(*tcb)->lastUsed>=age; )
-			{
-				threadcacheblk *f=*tcb;
-				size_t blksize=f->size; /*nedblksize(f);*/
-				assert(blksize<=nedblksize(f));
-				assert(blksize);
-#ifdef FULLSANITYCHECKS
-				assert(*(unsigned int *) "NEDN"==(*tcb)->magic);
-#endif
-				*tcb=(*tcb)->prev;
-				if(*tcb)
-					(*tcb)->next=0;
-				else
-					*tcbptr=0;
-				tc->freeInCache-=blksize;
-				assert((long) tc->freeInCache>=0);
-				mspace_free(0, f);
-				/*tcsanitycheck(tcbptr);*/
-			}
-		}
-	}
-#ifdef FULLSANITYCHECKS
-	tcfullsanitycheck(tc);
-#endif
-}
-static void DestroyCaches(nedpool *p) THROWSPEC
-{
-	{
-		threadcache *tc;
-		int n;
-		for(n=0; n<THREADCACHEMAXCACHES; n++)
-		{
-			if((tc=p->caches[n]))
-			{
-				tc->frees++;
-				RemoveCacheEntries(p, tc, 0);
-				assert(!tc->freeInCache);
-				tc->mymspace=-1;
-				tc->threadid=0;
-				mspace_free(0, tc);
-				p->caches[n]=0;
-			}
-		}
-	}
-}
-
-static NOINLINE threadcache *AllocCache(nedpool *p) THROWSPEC
-{
-	threadcache *tc=0;
-	int n, end;
-	ACQUIRE_LOCK(&p->mutex);
-	for(n=0; n<THREADCACHEMAXCACHES && p->caches[n]; n++);
-	if(THREADCACHEMAXCACHES==n)
-	{	/* List exhausted, so disable for this thread */
-		RELEASE_LOCK(&p->mutex);
-		return 0;
-	}
-	tc=p->caches[n]=(threadcache *) mspace_calloc(p->m[0], 1, sizeof(threadcache));
-	if(!tc)
-	{
-		RELEASE_LOCK(&p->mutex);
-		return 0;
-	}
-#ifdef FULLSANITYCHECKS
-	tc->magic1=*(unsigned int *)"NEDMALC1";
-	tc->magic2=*(unsigned int *)"NEDMALC2";
-#endif
-	tc->threadid=(long)(size_t)CURRENT_THREAD;
-	for(end=0; p->m[end]; end++);
-	tc->mymspace=tc->threadid % end;
-	RELEASE_LOCK(&p->mutex);
-	if(TLSSET(p->mycache, (void *)(size_t)(n+1))) abort();
-	return tc;
-}
-
-static void *threadcache_malloc(nedpool *p, threadcache *tc, size_t *size) THROWSPEC
-{
-	void *ret=0;
-	unsigned int bestsize;
-	unsigned int idx=size2binidx(*size);
-	size_t blksize=0;
-	threadcacheblk *blk, **binsptr;
-#ifdef FULLSANITYCHECKS
-	tcfullsanitycheck(tc);
-#endif
-	/* Calculate best fit bin size */
-	bestsize=1<<(idx+4);
-#if 0
-	/* Finer grained bin fit */
-	idx<<=1;
-	if(*size>bestsize)
-	{
-		idx++;
-		bestsize+=bestsize>>1;
-	}
-	if(*size>bestsize)
-	{
-		idx++;
-		bestsize=1<<(4+(idx>>1));
-	}
-#else
-	if(*size>bestsize)
-	{
-		idx++;
-		bestsize<<=1;
-	}
-#endif
-	assert(bestsize>=*size);
-	if(*size<bestsize) *size=bestsize;
-	assert(*size<=THREADCACHEMAX);
-	assert(idx<=THREADCACHEMAXBINS);
-	binsptr=&tc->bins[idx*2];
-	/* Try to match close, but move up a bin if necessary */
-	blk=*binsptr;
-	if(!blk || blk->size<*size)
-	{	/* Bump it up a bin */
-		if(idx<THREADCACHEMAXBINS)
-		{
-			idx++;
-			binsptr+=2;
-			blk=*binsptr;
-		}
-	}
-	if(blk)
-	{
-		blksize=blk->size; /*nedblksize(blk);*/
-		assert(nedblksize(blk)>=blksize);
-		assert(blksize>=*size);
-		if(blk->next)
-			blk->next->prev=0;
-		*binsptr=blk->next;
-		if(!*binsptr)
-			binsptr[1]=0;
-#ifdef FULLSANITYCHECKS
-		blk->magic=0;
-#endif
-		assert(binsptr[0]!=blk && binsptr[1]!=blk);
-		assert(nedblksize(blk)>=sizeof(threadcacheblk) && nedblksize(blk)<=THREADCACHEMAX+CHUNK_OVERHEAD);
-		/*printf("malloc: %p, %p, %p, %lu\n", p, tc, blk, (long) size);*/
-		ret=(void *) blk;
-	}
-	++tc->mallocs;
-	if(ret)
-	{
-		assert(blksize>=*size);
-		++tc->successes;
-		tc->freeInCache-=blksize;
-		assert((long) tc->freeInCache>=0);
-	}
-#if defined(DEBUG) && 0
-	if(!(tc->mallocs & 0xfff))
-	{
-		printf("*** threadcache=%u, mallocs=%u (%f), free=%u (%f), freeInCache=%u\n", (unsigned int) tc->threadid, tc->mallocs,
-			(float) tc->successes/tc->mallocs, tc->frees, (float) tc->successes/tc->frees, (unsigned int) tc->freeInCache);
-	}
-#endif
-#ifdef FULLSANITYCHECKS
-	tcfullsanitycheck(tc);
-#endif
-	return ret;
-}
-static NOINLINE void ReleaseFreeInCache(nedpool *p, threadcache *tc, int mymspace) THROWSPEC
-{
-	unsigned int age=THREADCACHEMAXFREESPACE/8192;
-	/*ACQUIRE_LOCK(&p->m[mymspace]->mutex);*/
-	while(age && tc->freeInCache>=THREADCACHEMAXFREESPACE)
-	{
-		RemoveCacheEntries(p, tc, age);
-		/*printf("*** Removing cache entries older than %u (%u)\n", age, (unsigned int) tc->freeInCache);*/
-		age>>=1;
-	}
-	/*RELEASE_LOCK(&p->m[mymspace]->mutex);*/
-}
-static void threadcache_free(nedpool *p, threadcache *tc, int mymspace, void *mem, size_t size) THROWSPEC
-{
-	unsigned int bestsize;
-	unsigned int idx=size2binidx(size);
-	threadcacheblk **binsptr, *tck=(threadcacheblk *) mem;
-	assert(size>=sizeof(threadcacheblk) && size<=THREADCACHEMAX+CHUNK_OVERHEAD);
-#ifdef DEBUG
-	{	/* Make sure this is a valid memory block */
-	    mchunkptr p  = mem2chunk(mem);
-	    mstate fm = get_mstate_for(p);
-	    if (!ok_magic(fm)) {
-	      USAGE_ERROR_ACTION(fm, p);
-	      return;
-	    }
-	}
-#endif
-#ifdef FULLSANITYCHECKS
-	tcfullsanitycheck(tc);
-#endif
-	/* Calculate best fit bin size */
-	bestsize=1<<(idx+4);
-#if 0
-	/* Finer grained bin fit */
-	idx<<=1;
-	if(size>bestsize)
-	{
-		unsigned int biggerbestsize=bestsize+bestsize<<1;
-		if(size>=biggerbestsize)
-		{
-			idx++;
-			bestsize=biggerbestsize;
-		}
-	}
-#endif
-	if(bestsize!=size)	/* dlmalloc can round up, so we round down to preserve indexing */
-		size=bestsize;
-	binsptr=&tc->bins[idx*2];
-	assert(idx<=THREADCACHEMAXBINS);
-	if(tck==*binsptr)
-	{
-		fprintf(stderr, "Attempt to free already freed memory block %p - aborting!\n", (void *)tck);
-		abort();
-	}
-#ifdef FULLSANITYCHECKS
-	tck->magic=*(unsigned int *) "NEDN";
-#endif
-	tck->lastUsed=++tc->frees;
-	tck->size=(unsigned int) size;
-	tck->next=*binsptr;
-	tck->prev=0;
-	if(tck->next)
-		tck->next->prev=tck;
-	else
-		binsptr[1]=tck;
-	assert(!*binsptr || (*binsptr)->size==tck->size);
-	*binsptr=tck;
-	assert(tck==tc->bins[idx*2]);
-	assert(tc->bins[idx*2+1]==tck || binsptr[0]->next->prev==tck);
-	/*printf("free: %p, %p, %p, %lu\n", p, tc, mem, (long) size);*/
-	tc->freeInCache+=size;
-#ifdef FULLSANITYCHECKS
-	tcfullsanitycheck(tc);
-#endif
-#if 1
-	if(tc->freeInCache>=THREADCACHEMAXFREESPACE)
-		ReleaseFreeInCache(p, tc, mymspace);
-#endif
-}
-
-
-
-
-static NOINLINE int InitPool(nedpool *p, size_t capacity, int threads) THROWSPEC
-{	/* threads is -1 for system pool */
-	ensure_initialization();
-	ACQUIRE_MALLOC_GLOBAL_LOCK();
-	if(p->threads) goto done;
-	if(INITIAL_LOCK(&p->mutex)) goto err;
-	if(TLSALLOC(&p->mycache)) goto err;
-	if(!(p->m[0]=(mstate) create_mspace(capacity, 1))) goto err;
-	p->m[0]->extp=p;
-	p->threads=(threads<1 || threads>MAXTHREADSINPOOL) ? MAXTHREADSINPOOL : threads;
-done:
-	RELEASE_MALLOC_GLOBAL_LOCK();
-	return 1;
-err:
-	if(threads<0)
-		abort();			/* If you can't allocate for system pool, we're screwed */
-	DestroyCaches(p);
-	if(p->m[0])
-	{
-		destroy_mspace(p->m[0]);
-		p->m[0]=0;
-	}
-	if(p->mycache)
-	{
-		if(TLSFREE(p->mycache)) abort();
-		p->mycache=0;
-	}
-	RELEASE_MALLOC_GLOBAL_LOCK();
-	return 0;
-}
-static NOINLINE mstate FindMSpace(nedpool *p, threadcache *tc, int *lastUsed, size_t size) THROWSPEC
-{	/* Gets called when thread's last used mspace is in use. The strategy
-	is to run through the list of all available mspaces looking for an
-	unlocked one and if we fail, we create a new one so long as we don't
-	exceed p->threads */
-	int n, end;
-	for(n=end=*lastUsed+1; p->m[n]; end=++n)
-	{
-		if(TRY_LOCK(&p->m[n]->mutex)) goto found;
-	}
-	for(n=0; n<*lastUsed && p->m[n]; n++)
-	{
-		if(TRY_LOCK(&p->m[n]->mutex)) goto found;
-	}
-	if(end<p->threads)
-	{
-		mstate temp;
-		if(!(temp=(mstate) create_mspace(size, 1)))
-			goto badexit;
-		/* Now we're ready to modify the lists, we lock */
-		ACQUIRE_LOCK(&p->mutex);
-		while(p->m[end] && end<p->threads)
-			end++;
-		if(end>=p->threads)
-		{	/* Drat, must destroy it now */
-			RELEASE_LOCK(&p->mutex);
-			destroy_mspace((mspace) temp);
-			goto badexit;
-		}
-		/* We really want to make sure this goes into memory now but we
-		have to be careful of breaking aliasing rules, so write it twice */
-		{
-			volatile struct malloc_state **_m=(volatile struct malloc_state **) &p->m[end];
-			*_m=(p->m[end]=temp);
-		}
-		ACQUIRE_LOCK(&p->m[end]->mutex);
-		/*printf("Created mspace idx %d\n", end);*/
-		RELEASE_LOCK(&p->mutex);
-		n=end;
-		goto found;
-	}
-	/* Let it lock on the last one it used */
-badexit:
-	ACQUIRE_LOCK(&p->m[*lastUsed]->mutex);
-	return p->m[*lastUsed];
-found:
-	*lastUsed=n;
-	if(tc)
-		tc->mymspace=n;
-	else
-	{
-		if(TLSSET(p->mycache, (void *)(size_t)(-(n+1)))) abort();
-	}
-	return p->m[n];
-}
-
-nedpool *nedcreatepool(size_t capacity, int threads) THROWSPEC
-{
-	nedpool *ret;
-	if(!(ret=(nedpool *) nedpcalloc(0, 1, sizeof(nedpool)))) return 0;
-	if(!InitPool(ret, capacity, threads))
-	{
-		nedpfree(0, ret);
-		return 0;
-	}
-	return ret;
-}
-void neddestroypool(nedpool *p) THROWSPEC
-{
-	int n;
-	ACQUIRE_LOCK(&p->mutex);
-	DestroyCaches(p);
-	for(n=0; p->m[n]; n++)
-	{
-		destroy_mspace(p->m[n]);
-		p->m[n]=0;
-	}
-	RELEASE_LOCK(&p->mutex);
-	if(TLSFREE(p->mycache)) abort();
-	nedpfree(0, p);
-}
-
-void nedpsetvalue(nedpool *p, void *v) THROWSPEC
-{
-	if(!p) { p=&syspool; if(!syspool.threads) InitPool(&syspool, 0, -1); }
-	p->uservalue=v;
-}
-void *nedgetvalue(nedpool **p, void *mem) THROWSPEC
-{
-	nedpool *np=0;
-	mchunkptr mcp=mem2chunk(mem);
-	mstate fm;
-	if(!(is_aligned(chunk2mem(mcp))) && mcp->head != FENCEPOST_HEAD) return 0;
-	if(!cinuse(mcp)) return 0;
-	if(!next_pinuse(mcp)) return 0;
-	if(!is_mmapped(mcp) && !pinuse(mcp))
-	{
-		if(next_chunk(prev_chunk(mcp))!=mcp) return 0;
-	}
-	fm=get_mstate_for(mcp);
-	if(!ok_magic(fm)) return 0;
-	if(!ok_address(fm, mcp)) return 0;
-	if(!fm->extp) return 0;
-	np=(nedpool *) fm->extp;
-	if(p) *p=np;
-	return np->uservalue;
-}
-
-void neddisablethreadcache(nedpool *p) THROWSPEC
-{
-	int mycache;
-	if(!p)
-	{
-		p=&syspool;
-		if(!syspool.threads) InitPool(&syspool, 0, -1);
-	}
-	mycache=(int)(size_t) TLSGET(p->mycache);
-	if(!mycache)
-	{	/* Set to mspace 0 */
-		if(TLSSET(p->mycache, (void *)-1)) abort();
-	}
-	else if(mycache>0)
-	{	/* Set to last used mspace */
-		threadcache *tc=p->caches[mycache-1];
-#if defined(DEBUG)
-		printf("Threadcache utilisation: %lf%% in cache with %lf%% lost to other threads\n",
-			100.0*tc->successes/tc->mallocs, 100.0*((double) tc->mallocs-tc->frees)/tc->mallocs);
-#endif
-		if(TLSSET(p->mycache, (void *)(size_t)(-tc->mymspace))) abort();
-		tc->frees++;
-		RemoveCacheEntries(p, tc, 0);
-		assert(!tc->freeInCache);
-		tc->mymspace=-1;
-		tc->threadid=0;
-		mspace_free(0, p->caches[mycache-1]);
-		p->caches[mycache-1]=0;
-	}
-}
-
-#define GETMSPACE(m,p,tc,ms,s,action)           \
-  do                                            \
-  {                                             \
-    mstate m = GetMSpace((p),(tc),(ms),(s));    \
-    action;                                     \
-    RELEASE_LOCK(&m->mutex);                    \
-  } while (0)
-
-static FORCEINLINE mstate GetMSpace(nedpool *p, threadcache *tc, int mymspace, size_t size) THROWSPEC
-{	/* Returns a locked and ready for use mspace */
-	mstate m=p->m[mymspace];
-	assert(m);
-	if(!TRY_LOCK(&p->m[mymspace]->mutex)) m=FindMSpace(p, tc, &mymspace, size);\
-	/*assert(IS_LOCKED(&p->m[mymspace]->mutex));*/
-	return m;
-}
-static FORCEINLINE void GetThreadCache(nedpool **p, threadcache **tc, int *mymspace, size_t *size) THROWSPEC
-{
-	int mycache;
-	if(size && *size<sizeof(threadcacheblk)) *size=sizeof(threadcacheblk);
-	if(!*p)
-	{
-		*p=&syspool;
-		if(!syspool.threads) InitPool(&syspool, 0, -1);
-	}
-	mycache=(int)(size_t) TLSGET((*p)->mycache);
-	if(mycache>0)
-	{
-		*tc=(*p)->caches[mycache-1];
-		*mymspace=(*tc)->mymspace;
-	}
-	else if(!mycache)
-	{
-		*tc=AllocCache(*p);
-		if(!*tc)
-		{	/* Disable */
-			if(TLSSET((*p)->mycache, (void *)-1)) abort();
-			*mymspace=0;
-		}
-		else
-			*mymspace=(*tc)->mymspace;
-	}
-	else
-	{
-		*tc=0;
-		*mymspace=-mycache-1;
-	}
-	assert(*mymspace>=0);
-	assert((long)(size_t)CURRENT_THREAD==(*tc)->threadid);
-#ifdef FULLSANITYCHECKS
-	if(*tc)
-	{
-		if(*(unsigned int *)"NEDMALC1"!=(*tc)->magic1 || *(unsigned int *)"NEDMALC2"!=(*tc)->magic2)
-		{
-			abort();
-		}
-	}
-#endif
-}
-
-void * nedpmalloc(nedpool *p, size_t size) THROWSPEC
-{
-	void *ret=0;
-	threadcache *tc;
-	int mymspace;
-	GetThreadCache(&p, &tc, &mymspace, &size);
-#if THREADCACHEMAX
-	if(tc && size<=THREADCACHEMAX)
-	{	/* Use the thread cache */
-		ret=threadcache_malloc(p, tc, &size);
-	}
-#endif
-	if(!ret)
-	{	/* Use this thread's mspace */
-	GETMSPACE(m, p, tc, mymspace, size,
-		  ret=mspace_malloc(m, size));
-	}
-	return ret;
-}
-void * nedpcalloc(nedpool *p, size_t no, size_t size) THROWSPEC
-{
-	size_t rsize=size*no;
-	void *ret=0;
-	threadcache *tc;
-	int mymspace;
-	GetThreadCache(&p, &tc, &mymspace, &rsize);
-#if THREADCACHEMAX
-	if(tc && rsize<=THREADCACHEMAX)
-	{	/* Use the thread cache */
-		if((ret=threadcache_malloc(p, tc, &rsize)))
-			memset(ret, 0, rsize);
-	}
-#endif
-	if(!ret)
-	{	/* Use this thread's mspace */
-	GETMSPACE(m, p, tc, mymspace, rsize,
-		  ret=mspace_calloc(m, 1, rsize));
-	}
-	return ret;
-}
-void * nedprealloc(nedpool *p, void *mem, size_t size) THROWSPEC
-{
-	void *ret=0;
-	threadcache *tc;
-	int mymspace;
-	if(!mem) return nedpmalloc(p, size);
-	GetThreadCache(&p, &tc, &mymspace, &size);
-#if THREADCACHEMAX
-	if(tc && size && size<=THREADCACHEMAX)
-	{	/* Use the thread cache */
-		size_t memsize=nedblksize(mem);
-		assert(memsize);
-		if((ret=threadcache_malloc(p, tc, &size)))
-		{
-			memcpy(ret, mem, memsize<size ? memsize : size);
-			if(memsize<=THREADCACHEMAX)
-				threadcache_free(p, tc, mymspace, mem, memsize);
-			else
-				mspace_free(0, mem);
-		}
-	}
-#endif
-	if(!ret)
-	{	/* Reallocs always happen in the mspace they happened in, so skip
-		locking the preferred mspace for this thread */
-		ret=mspace_realloc(0, mem, size);
-	}
-	return ret;
-}
-void   nedpfree(nedpool *p, void *mem) THROWSPEC
-{	/* Frees always happen in the mspace they happened in, so skip
-	locking the preferred mspace for this thread */
-	threadcache *tc;
-	int mymspace;
-	size_t memsize;
-	assert(mem);
-	GetThreadCache(&p, &tc, &mymspace, 0);
-#if THREADCACHEMAX
-	memsize=nedblksize(mem);
-	assert(memsize);
-	if(mem && tc && memsize<=(THREADCACHEMAX+CHUNK_OVERHEAD))
-		threadcache_free(p, tc, mymspace, mem, memsize);
-	else
-#endif
-		mspace_free(0, mem);
-}
-void * nedpmemalign(nedpool *p, size_t alignment, size_t bytes) THROWSPEC
-{
-	void *ret;
-	threadcache *tc;
-	int mymspace;
-	GetThreadCache(&p, &tc, &mymspace, &bytes);
-	{	/* Use this thread's mspace */
-	GETMSPACE(m, p, tc, mymspace, bytes,
-		  ret=mspace_memalign(m, alignment, bytes));
-	}
-	return ret;
-}
-#if !NO_MALLINFO
-struct mallinfo nedpmallinfo(nedpool *p) THROWSPEC
-{
-	int n;
-	struct mallinfo ret={0};
-	if(!p) { p=&syspool; if(!syspool.threads) InitPool(&syspool, 0, -1); }
-	for(n=0; p->m[n]; n++)
-	{
-		struct mallinfo t=mspace_mallinfo(p->m[n]);
-		ret.arena+=t.arena;
-		ret.ordblks+=t.ordblks;
-		ret.hblkhd+=t.hblkhd;
-		ret.usmblks+=t.usmblks;
-		ret.uordblks+=t.uordblks;
-		ret.fordblks+=t.fordblks;
-		ret.keepcost+=t.keepcost;
-	}
-	return ret;
-}
-#endif
-int    nedpmallopt(nedpool *p, int parno, int value) THROWSPEC
-{
-	return mspace_mallopt(parno, value);
-}
-int    nedpmalloc_trim(nedpool *p, size_t pad) THROWSPEC
-{
-	int n, ret=0;
-	if(!p) { p=&syspool; if(!syspool.threads) InitPool(&syspool, 0, -1); }
-	for(n=0; p->m[n]; n++)
-	{
-		ret+=mspace_trim(p->m[n], pad);
-	}
-	return ret;
-}
-void   nedpmalloc_stats(nedpool *p) THROWSPEC
-{
-	int n;
-	if(!p) { p=&syspool; if(!syspool.threads) InitPool(&syspool, 0, -1); }
-	for(n=0; p->m[n]; n++)
-	{
-		mspace_malloc_stats(p->m[n]);
-	}
-}
-size_t nedpmalloc_footprint(nedpool *p) THROWSPEC
-{
-	size_t ret=0;
-	int n;
-	if(!p) { p=&syspool; if(!syspool.threads) InitPool(&syspool, 0, -1); }
-	for(n=0; p->m[n]; n++)
-	{
-		ret+=mspace_footprint(p->m[n]);
-	}
-	return ret;
-}
-void **nedpindependent_calloc(nedpool *p, size_t elemsno, size_t elemsize, void **chunks) THROWSPEC
-{
-	void **ret;
-	threadcache *tc;
-	int mymspace;
-	GetThreadCache(&p, &tc, &mymspace, &elemsize);
-    GETMSPACE(m, p, tc, mymspace, elemsno*elemsize,
-	      ret=mspace_independent_calloc(m, elemsno, elemsize, chunks));
-	return ret;
-}
-void **nedpindependent_comalloc(nedpool *p, size_t elems, size_t *sizes, void **chunks) THROWSPEC
-{
-	void **ret;
-	threadcache *tc;
-	int mymspace;
-	size_t i, *adjustedsizes=(size_t *) alloca(elems*sizeof(size_t));
-	if(!adjustedsizes) return 0;
-	for(i=0; i<elems; i++)
-		adjustedsizes[i]=sizes[i]<sizeof(threadcacheblk) ? sizeof(threadcacheblk) : sizes[i];
-	GetThreadCache(&p, &tc, &mymspace, 0);
-	GETMSPACE(m, p, tc, mymspace, 0,
-	      ret=mspace_independent_comalloc(m, elems, adjustedsizes, chunks));
-	return ret;
-}
-
-#if defined(__cplusplus)
-}
-#endif
diff --git a/compat/nedmalloc/nedmalloc.h b/compat/nedmalloc/nedmalloc.h
deleted file mode 100644
index f960e66..0000000
--- a/compat/nedmalloc/nedmalloc.h
+++ /dev/null
@@ -1,180 +0,0 @@
-/* nedalloc, an alternative malloc implementation for multiple threads without
-lock contention based on dlmalloc v2.8.3. (C) 2005 Niall Douglas
-
-Boost Software License - Version 1.0 - August 17th, 2003
-
-Permission is hereby granted, free of charge, to any person or organization
-obtaining a copy of the software and accompanying documentation covered by
-this license (the "Software") to use, reproduce, display, distribute,
-execute, and transmit the Software, and to prepare derivative works of the
-Software, and to permit third-parties to whom the Software is furnished to
-do so, all subject to the following:
-
-The copyright notices in the Software and this entire statement, including
-the above license grant, this restriction and the following disclaimer,
-must be included in all copies of the Software, in whole or in part, and
-all derivative works of the Software, unless such copies or derivative
-works are solely in the form of machine-executable object code generated by
-a source language processor.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
-SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
-FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
-ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
-DEALINGS IN THE SOFTWARE.
-*/
-
-#ifndef NEDMALLOC_H
-#define NEDMALLOC_H
-
-
-/* See malloc.c.h for what each function does.
-
-REPLACE_SYSTEM_ALLOCATOR causes nedalloc's functions to be called malloc,
-free etc. instead of nedmalloc, nedfree etc. You may or may not want this.
-
-NO_NED_NAMESPACE prevents the functions from being defined in the nedalloc
-namespace when in C++ (uses the global namespace instead).
-
-EXTSPEC can be defined to be __declspec(dllexport) or
-__attribute__ ((visibility("default"))) or whatever you like. It defaults
-to extern.
-
-USE_LOCKS can be 2 if you want to define your own MLOCK_T, INITIAL_LOCK,
-ACQUIRE_LOCK, RELEASE_LOCK, TRY_LOCK, IS_LOCKED and NULL_LOCK_INITIALIZER.
-
-*/
-
-#include <stddef.h>   /* for size_t */
-
-#ifndef EXTSPEC
- #define EXTSPEC extern
-#endif
-
-#if defined(_MSC_VER) && _MSC_VER>=1400
- #define MALLOCATTR __declspec(restrict)
-#endif
-#ifdef __GNUC__
- #define MALLOCATTR __attribute__ ((malloc))
-#endif
-#ifndef MALLOCATTR
- #define MALLOCATTR
-#endif
-
-#ifdef REPLACE_SYSTEM_ALLOCATOR
- #define nedmalloc               malloc
- #define nedcalloc               calloc
- #define nedrealloc              realloc
- #define nedfree                 free
- #define nedmemalign             memalign
- #define nedmallinfo             mallinfo
- #define nedmallopt              mallopt
- #define nedmalloc_trim          malloc_trim
- #define nedmalloc_stats         malloc_stats
- #define nedmalloc_footprint     malloc_footprint
- #define nedindependent_calloc   independent_calloc
- #define nedindependent_comalloc independent_comalloc
- #ifdef _MSC_VER
-  #define nedblksize              _msize
- #endif
-#endif
-
-#ifndef NO_MALLINFO
-#define NO_MALLINFO 0
-#endif
-
-#if !NO_MALLINFO
-struct mallinfo;
-#endif
-
-#if defined(__cplusplus)
- #if !defined(NO_NED_NAMESPACE)
-namespace nedalloc {
- #else
-extern "C" {
- #endif
- #define THROWSPEC throw()
-#else
- #define THROWSPEC
-#endif
-
-/* These are the global functions */
-
-/* Gets the usable size of an allocated block. Note this will always be bigger than what was
-asked for due to rounding etc.
-*/
-EXTSPEC size_t nedblksize(void *mem) THROWSPEC;
-
-EXTSPEC void nedsetvalue(void *v) THROWSPEC;
-
-EXTSPEC MALLOCATTR void * nedmalloc(size_t size) THROWSPEC;
-EXTSPEC MALLOCATTR void * nedcalloc(size_t no, size_t size) THROWSPEC;
-EXTSPEC MALLOCATTR void * nedrealloc(void *mem, size_t size) THROWSPEC;
-EXTSPEC void   nedfree(void *mem) THROWSPEC;
-EXTSPEC MALLOCATTR void * nedmemalign(size_t alignment, size_t bytes) THROWSPEC;
-#if !NO_MALLINFO
-EXTSPEC struct mallinfo nedmallinfo(void) THROWSPEC;
-#endif
-EXTSPEC int    nedmallopt(int parno, int value) THROWSPEC;
-EXTSPEC int    nedmalloc_trim(size_t pad) THROWSPEC;
-EXTSPEC void   nedmalloc_stats(void) THROWSPEC;
-EXTSPEC size_t nedmalloc_footprint(void) THROWSPEC;
-EXTSPEC MALLOCATTR void **nedindependent_calloc(size_t elemsno, size_t elemsize, void **chunks) THROWSPEC;
-EXTSPEC MALLOCATTR void **nedindependent_comalloc(size_t elems, size_t *sizes, void **chunks) THROWSPEC;
-
-/* These are the pool functions */
-struct nedpool_t;
-typedef struct nedpool_t nedpool;
-
-/* Creates a memory pool for use with the nedp* functions below.
-Capacity is how much to allocate immediately (if you know you'll be allocating a lot
-of memory very soon) which you can leave at zero. Threads specifies how many threads
-will *normally* be accessing the pool concurrently. Setting this to zero means it
-extends on demand, but be careful of this as it can rapidly consume system resources
-where bursts of concurrent threads use a pool at once.
-*/
-EXTSPEC MALLOCATTR nedpool *nedcreatepool(size_t capacity, int threads) THROWSPEC;
-
-/* Destroys a memory pool previously created by nedcreatepool().
-*/
-EXTSPEC void neddestroypool(nedpool *p) THROWSPEC;
-
-/* Sets a value to be associated with a pool. You can retrieve this value by passing
-any memory block allocated from that pool.
-*/
-EXTSPEC void nedpsetvalue(nedpool *p, void *v) THROWSPEC;
-/* Gets a previously set value using nedpsetvalue() or zero if memory is unknown.
-Optionally can also retrieve pool.
-*/
-EXTSPEC void *nedgetvalue(nedpool **p, void *mem) THROWSPEC;
-
-/* Disables the thread cache for the calling thread, returning any existing cache
-data to the central pool.
-*/
-EXTSPEC void neddisablethreadcache(nedpool *p) THROWSPEC;
-
-EXTSPEC MALLOCATTR void * nedpmalloc(nedpool *p, size_t size) THROWSPEC;
-EXTSPEC MALLOCATTR void * nedpcalloc(nedpool *p, size_t no, size_t size) THROWSPEC;
-EXTSPEC MALLOCATTR void * nedprealloc(nedpool *p, void *mem, size_t size) THROWSPEC;
-EXTSPEC void   nedpfree(nedpool *p, void *mem) THROWSPEC;
-EXTSPEC MALLOCATTR void * nedpmemalign(nedpool *p, size_t alignment, size_t bytes) THROWSPEC;
-#if !NO_MALLINFO
-EXTSPEC struct mallinfo nedpmallinfo(nedpool *p) THROWSPEC;
-#endif
-EXTSPEC int    nedpmallopt(nedpool *p, int parno, int value) THROWSPEC;
-EXTSPEC int    nedpmalloc_trim(nedpool *p, size_t pad) THROWSPEC;
-EXTSPEC void   nedpmalloc_stats(nedpool *p) THROWSPEC;
-EXTSPEC size_t nedpmalloc_footprint(nedpool *p) THROWSPEC;
-EXTSPEC MALLOCATTR void **nedpindependent_calloc(nedpool *p, size_t elemsno, size_t elemsize, void **chunks) THROWSPEC;
-EXTSPEC MALLOCATTR void **nedpindependent_comalloc(nedpool *p, size_t elems, size_t *sizes, void **chunks) THROWSPEC;
-
-#if defined(__cplusplus)
-}
-#endif
-
-#undef MALLOCATTR
-#undef EXTSPEC
-
-#endif
diff --git a/compat/posix.h b/compat/posix.h
index 94699a0..faaae1b 100644
--- a/compat/posix.h
+++ b/compat/posix.h
@@ -137,9 +137,6 @@
 #include <sys/socket.h>
 #include <sys/ioctl.h>
 #include <sys/statvfs.h>
-#ifndef NO_WRITEV
-#include <sys/uio.h>
-#endif
 #include <termios.h>
 #ifndef NO_SYS_SELECT_H
 #include <sys/select.h>
@@ -326,17 +323,6 @@ int git_lstat(const char *, struct stat *);
 ssize_t git_pread(int fd, void *buf, size_t count, off_t offset);
 #endif
 
-#ifdef NO_WRITEV
-#define writev git_writev
-#define iovec git_iovec
-struct git_iovec {
-	void *iov_base;
-	size_t iov_len;
-};
-
-ssize_t git_writev(int fd, const struct iovec *iov, int iovcnt);
-#endif
-
 #ifdef NO_SETENV
 #define setenv gitsetenv
 int gitsetenv(const char *, const char *, int);
diff --git a/compat/win32/pthread.c b/compat/win32/pthread.c
index 7e93146..398caa9 100644
--- a/compat/win32/pthread.c
+++ b/compat/win32/pthread.c
@@ -66,3 +66,29 @@ int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)
 		return err_win_to_posix(GetLastError());
 	return 0;
 }
+
+int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex,
+			   const struct timespec *abstime)
+{
+	struct timeval now;
+	long long now_ms, deadline_ms;
+	DWORD timeout_ms;
+
+	gettimeofday(&now, NULL);
+	now_ms = (long long)now.tv_sec * 1000 + now.tv_usec / 1000;
+	deadline_ms = (long long)abstime->tv_sec * 1000 +
+		      abstime->tv_nsec / 1000000;
+
+	if (deadline_ms <= now_ms)
+		return ETIMEDOUT;
+	else
+		timeout_ms = (DWORD)(deadline_ms - now_ms);
+
+	if (SleepConditionVariableCS(cond, mutex, timeout_ms) == 0) {
+		DWORD err = GetLastError();
+		if (err == ERROR_TIMEOUT)
+			return ETIMEDOUT;
+		return err_win_to_posix(err);
+	}
+	return 0;
+}
diff --git a/compat/win32/pthread.h b/compat/win32/pthread.h
index ccacc5a..d80df8d 100644
--- a/compat/win32/pthread.h
+++ b/compat/win32/pthread.h
@@ -64,6 +64,8 @@ int win32_pthread_join(pthread_t *thread, void **value_ptr);
 pthread_t pthread_self(void);
 
 int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);
+int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex,
+			   const struct timespec *abstime);
 
 static inline void NORETURN pthread_exit(void *ret)
 {
diff --git a/compat/writev.c b/compat/writev.c
deleted file mode 100644
index 3a94870..0000000
--- a/compat/writev.c
+++ /dev/null
@@ -1,44 +0,0 @@
-#include "../git-compat-util.h"
-#include "../wrapper.h"
-
-ssize_t git_writev(int fd, const struct iovec *iov, int iovcnt)
-{
-	size_t total_written = 0;
-	size_t sum = 0;
-
-	/*
-	 * According to writev(3p), the syscall shall error with EINVAL in case
-	 * the sum of `iov_len` overflows `ssize_t`.
-	 */
-	 for (int i = 0; i < iovcnt; i++) {
-		if (iov[i].iov_len > maximum_signed_value_of_type(ssize_t) ||
-		    iov[i].iov_len + sum > maximum_signed_value_of_type(ssize_t)) {
-			errno = EINVAL;
-			return -1;
-		}
-
-		sum += iov[i].iov_len;
-	}
-
-	for (int i = 0; i < iovcnt; i++) {
-		const char *bytes = iov[i].iov_base;
-		size_t iovec_written = 0;
-
-		while (iovec_written < iov[i].iov_len) {
-			ssize_t bytes_written = xwrite(fd, bytes + iovec_written,
-						       iov[i].iov_len - iovec_written);
-			if (bytes_written < 0) {
-				if (total_written)
-					goto out;
-				return bytes_written;
-			}
-			if (!bytes_written)
-				goto out;
-			iovec_written += bytes_written;
-			total_written += bytes_written;
-		}
-	}
-
-out:
-	return (ssize_t) total_written;
-}
diff --git a/compat/zlib-compat.h b/compat/zlib-compat.h
index ac08276..5078c5e 100644
--- a/compat/zlib-compat.h
+++ b/compat/zlib-compat.h
@@ -7,6 +7,8 @@
 # define z_stream_s zng_stream_s
 # define gz_header_s zng_gz_header_s
 
+# define adler32(adler, buf, len) zng_adler32(adler, buf, len)
+
 # define crc32(crc, buf, len) zng_crc32(crc, buf, len)
 
 # define inflate(strm, bits) zng_inflate(strm, bits)
diff --git a/config.c b/config.c
index 156f2a2..a1b92fe 100644
--- a/config.c
+++ b/config.c
@@ -1212,6 +1212,15 @@ int git_config_int(const char *name, const char *value,
 	return ret;
 }
 
+unsigned int git_config_uint(const char *name, const char *value,
+			     const struct key_value_info *kvi)
+{
+	unsigned int ret;
+	if (!git_parse_uint(value, &ret))
+		die_bad_number(name, value, kvi);
+	return ret;
+}
+
 int64_t git_config_int64(const char *name, const char *value,
 			 const struct key_value_info *kvi)
 {
@@ -1907,6 +1916,18 @@ int git_configset_get_int(struct config_set *set, const char *key, int *dest)
 		return 1;
 }
 
+int git_configset_get_uint(struct config_set *set, const char *key, unsigned int *dest)
+{
+	const char *value;
+	struct key_value_info kvi;
+
+	if (!git_configset_get_value(set, key, &value, &kvi)) {
+		*dest = git_config_uint(key, value, &kvi);
+		return 0;
+	} else
+		return 1;
+}
+
 int git_configset_get_ulong(struct config_set *set, const char *key, unsigned long *dest)
 {
 	const char *value;
@@ -2356,6 +2377,13 @@ int repo_config_get_int(struct repository *repo,
 	return git_configset_get_int(repo->config, key, dest);
 }
 
+int repo_config_get_uint(struct repository *repo,
+			 const char *key, unsigned int *dest)
+{
+	git_config_check_init(repo);
+	return git_configset_get_uint(repo->config, key, dest);
+}
+
 int repo_config_get_ulong(struct repository *repo,
 			  const char *key, unsigned long *dest)
 {
diff --git a/config.h b/config.h
index ba426a9..bf47fb3 100644
--- a/config.h
+++ b/config.h
@@ -268,6 +268,12 @@ int64_t git_config_int64(const char *, const char *,
 			 const struct key_value_info *);
 
 /**
+ * Identical to `git_config_int`, but for unsigned ints.
+ */
+unsigned int git_config_uint(const char *, const char *,
+			     const struct key_value_info *);
+
+/**
  * Identical to `git_config_int`, but for unsigned longs.
  */
 unsigned long git_config_ulong(const char *, const char *,
@@ -560,6 +566,7 @@ int git_configset_get_value(struct config_set *cs, const char *key,
 
 int git_configset_get_string(struct config_set *cs, const char *key, char **dest);
 int git_configset_get_int(struct config_set *cs, const char *key, int *dest);
+int git_configset_get_uint(struct config_set *cs, const char *key, unsigned int *dest);
 int git_configset_get_ulong(struct config_set *cs, const char *key, unsigned long *dest);
 int git_configset_get_bool(struct config_set *cs, const char *key, int *dest);
 int git_configset_get_bool_or_int(struct config_set *cs, const char *key, int *is_bool, int *dest);
@@ -651,6 +658,12 @@ int repo_config_get_string_tmp(struct repository *r,
 int repo_config_get_int(struct repository *r, const char *key, int *dest);
 
 /**
+ * Similar to `repo_config_get_int` but for unsigned ints.
+ */
+int repo_config_get_uint(struct repository *r,
+			 const char *key, unsigned int *dest);
+
+/**
  * Similar to `repo_config_get_int` but for unsigned longs.
  */
 int repo_config_get_ulong(struct repository *r,
diff --git a/config.mak.dev b/config.mak.dev
index c8dcf78..eecb12c 100644
--- a/config.mak.dev
+++ b/config.mak.dev
@@ -98,6 +98,13 @@
 endif
 endif
 
+# glibc 2.43 headers unconditionally use _Generic even when we ask the
+# compiler to stick to -std=gnu99 and unlike GCC, clang lacks a
+# workaround to squelch warnings from system headers.
+ifneq ($(filter clang1,$(COMPILER_FEATURES)),)     # if we are using clang
+DEVELOPER_CFLAGS += -Wno-c11-extensions
+endif
+
 # https://bugzilla.redhat.com/show_bug.cgi?id=2075786
 ifneq ($(filter gcc12,$(COMPILER_FEATURES)),)
 DEVELOPER_CFLAGS += -Wno-error=stringop-overread
diff --git a/config.mak.uname b/config.mak.uname
index ccb3f718..f9a5ad9 100644
--- a/config.mak.uname
+++ b/config.mak.uname
@@ -63,11 +63,22 @@
 	PROCFS_EXECUTABLE_PATH = /proc/self/exe
 	HAVE_PLATFORM_PROCINFO = YesPlease
 	COMPAT_OBJS += compat/linux/procinfo.o
+	EXTLIBS += -ldl
 	# centos7/rhel7 provides gcc 4.8.5 and zlib 1.2.7.
         ifneq ($(findstring .el7.,$(uname_R)),)
 		BASIC_CFLAGS += -std=c99
         endif
 	LINK_FUZZ_PROGRAMS = YesPlease
+
+	# The builtin FSMonitor on Linux builds upon Simple-IPC.  Both require
+	# Unix domain sockets and PThreads.
+        ifndef NO_PTHREADS
+        ifndef NO_UNIX_SOCKETS
+	FSMONITOR_DAEMON_BACKEND = linux
+	FSMONITOR_OS_SETTINGS = unix
+	BASIC_CFLAGS += -DHAVE_LINUX_MAGIC_H
+        endif
+        endif
 endif
 ifeq ($(uname_S),GNU/kFreeBSD)
 	HAVE_ALLOCA_H = YesPlease
@@ -167,7 +178,7 @@
         ifndef NO_PTHREADS
         ifndef NO_UNIX_SOCKETS
 	FSMONITOR_DAEMON_BACKEND = darwin
-	FSMONITOR_OS_SETTINGS = darwin
+	FSMONITOR_OS_SETTINGS = unix
         endif
         endif
 
@@ -459,7 +470,6 @@
 	SANE_TOOL_PATH ?= $(msvc_bin_dir_msys)
 	HAVE_ALLOCA_H = YesPlease
 	NO_PREAD = YesPlease
-	NO_WRITEV = YesPlease
 	NEEDS_CRYPTO_WITH_SSL = YesPlease
 	NO_LIBGEN_H = YesPlease
 	NO_POLL = YesPlease
@@ -492,7 +502,6 @@
 	USE_WIN32_IPC = YesPlease
 	USE_WIN32_MMAP = YesPlease
 	MMAP_PREVENTS_DELETE = UnfortunatelyYes
-	# USE_NED_ALLOCATOR = YesPlease
 	UNRELIABLE_FSTAT = UnfortunatelyYes
 	OBJECT_CREATION_USES_RENAMES = UnfortunatelyNeedsTo
 	NO_REGEX = YesPlease
@@ -675,7 +684,6 @@
 	pathsep = ;
 	HAVE_ALLOCA_H = YesPlease
 	NO_PREAD = YesPlease
-	NO_WRITEV = YesPlease
 	NEEDS_CRYPTO_WITH_SSL = YesPlease
 	NO_LIBGEN_H = YesPlease
 	NO_POLL = YesPlease
@@ -760,9 +768,6 @@
 	HAVE_LIBCHARSET_H = YesPlease
 	USE_GETTEXT_SCHEME = fallthrough
 	USE_LIBPCRE = YesPlease
-        ifneq (CLANGARM64,$(MSYSTEM))
-		USE_NED_ALLOCATOR = YesPlease
-        endif
         ifeq (/mingw64,$(subst 32,64,$(subst clangarm,mingw,$(prefix))))
 		# Move system config into top-level /etc/
 		ETC_GITCONFIG = ../etc/gitconfig
diff --git a/connect.c b/connect.c
index a02583a..47e39d2 100644
--- a/connect.c
+++ b/connect.c
@@ -700,51 +700,21 @@ int server_supports(const char *feature)
 	return !!server_feature_value(feature, NULL);
 }
 
-enum protocol {
-	PROTO_LOCAL = 1,
-	PROTO_FILE,
-	PROTO_SSH,
-	PROTO_GIT
-};
-
-int url_is_local_not_ssh(const char *url)
+static const char *url_scheme_name(enum url_scheme scheme)
 {
-	const char *colon = strchr(url, ':');
-	const char *slash = strchr(url, '/');
-	return !colon || (slash && slash < colon) ||
-		(has_dos_drive_prefix(url) && is_valid_path(url));
-}
-
-static const char *prot_name(enum protocol protocol)
-{
-	switch (protocol) {
-		case PROTO_LOCAL:
-		case PROTO_FILE:
+	switch (scheme) {
+		case URL_SCHEME_LOCAL:
+		case URL_SCHEME_FILE:
 			return "file";
-		case PROTO_SSH:
+		case URL_SCHEME_SSH:
 			return "ssh";
-		case PROTO_GIT:
+		case URL_SCHEME_GIT:
 			return "git";
 		default:
 			return "unknown protocol";
 	}
 }
 
-static enum protocol get_protocol(const char *name)
-{
-	if (!strcmp(name, "ssh"))
-		return PROTO_SSH;
-	if (!strcmp(name, "git"))
-		return PROTO_GIT;
-	if (!strcmp(name, "git+ssh")) /* deprecated - do not use */
-		return PROTO_SSH;
-	if (!strcmp(name, "ssh+git")) /* deprecated - do not use */
-		return PROTO_SSH;
-	if (!strcmp(name, "file"))
-		return PROTO_FILE;
-	die(_("protocol '%s' is not supported"), name);
-}
-
 static char *host_end(char **hoststart, int removebrackets)
 {
 	char *host = *hoststart;
@@ -1081,14 +1051,14 @@ static char *get_port(char *host)
  * Extract protocol and relevant parts from the specified connection URL.
  * The caller must free() the returned strings.
  */
-static enum protocol parse_connect_url(const char *url_orig, char **ret_host,
-				       char **ret_path)
+static enum url_scheme parse_connect_url(const char *url_orig, char **ret_host,
+					 char **ret_path)
 {
 	char *url;
 	char *host, *path;
 	char *end;
 	int separator = '/';
-	enum protocol protocol = PROTO_LOCAL;
+	enum url_scheme scheme = URL_SCHEME_LOCAL;
 
 	if (is_url(url_orig))
 		url = url_decode(url_orig);
@@ -1098,12 +1068,14 @@ static enum protocol parse_connect_url(const char *url_orig, char **ret_host,
 	host = strstr(url, "://");
 	if (host) {
 		*host = '\0';
-		protocol = get_protocol(url);
+		scheme = url_get_scheme(url);
+		if (scheme == URL_SCHEME_UNKNOWN)
+			die(_("protocol '%s' is not supported"), url);
 		host += 3;
 	} else {
 		host = url;
 		if (!url_is_local_not_ssh(url)) {
-			protocol = PROTO_SSH;
+			scheme = URL_SCHEME_SSH;
 			separator = ':';
 		}
 	}
@@ -1114,13 +1086,13 @@ static enum protocol parse_connect_url(const char *url_orig, char **ret_host,
 	 */
 	end = host_end(&host, 0);
 
-	if (protocol == PROTO_LOCAL)
+	if (scheme == URL_SCHEME_LOCAL)
 		path = end;
-	else if (protocol == PROTO_FILE && *host != '/' &&
+	else if (scheme == URL_SCHEME_FILE && *host != '/' &&
 		 !has_dos_drive_prefix(host) &&
 		 offset_1st_component(host - 2) > 1)
 		path = host - 2; /* include the leading "//" */
-	else if (protocol == PROTO_FILE && has_dos_drive_prefix(end))
+	else if (scheme == URL_SCHEME_FILE && has_dos_drive_prefix(end))
 		path = end; /* "file://$(pwd)" may be "file://C:/projects/repo" */
 	else
 		path = strchr(end, separator);
@@ -1136,7 +1108,7 @@ static enum protocol parse_connect_url(const char *url_orig, char **ret_host,
 	end = path; /* Need to \0 terminate host here */
 	if (separator == ':')
 		path++; /* path starts after ':' */
-	if (protocol == PROTO_GIT || protocol == PROTO_SSH) {
+	if (scheme == URL_SCHEME_GIT || scheme == URL_SCHEME_SSH) {
 		if (path[1] == '~')
 			path++;
 	}
@@ -1147,7 +1119,7 @@ static enum protocol parse_connect_url(const char *url_orig, char **ret_host,
 	*ret_host = xstrdup(host);
 	*ret_path = path;
 	free(url);
-	return protocol;
+	return scheme;
 }
 
 static const char *get_ssh_command(void)
@@ -1427,12 +1399,12 @@ static void fill_ssh_args(struct child_process *conn, const char *ssh_host,
  * the connection failed).
  */
 struct child_process *git_connect(int fd[2], const char *url,
-				  const char *name,
+				  enum git_connect_service service,
 				  const char *prog, int flags)
 {
 	char *hostandport, *path;
 	struct child_process *conn;
-	enum protocol protocol;
+	enum url_scheme scheme;
 	enum protocol_version version = get_protocol_version_config();
 
 	/*
@@ -1441,7 +1413,7 @@ struct child_process *git_connect(int fd[2], const char *url,
 	 * fetch, ls-remote, etc), then fallback to v0 since we don't know how
 	 * to do anything else (like push or remote archive) via v2.
 	 */
-	if (version == protocol_v2 && strcmp("git-upload-pack", name))
+	if (version == protocol_v2 && service != GIT_CONNECT_UPLOAD_PACK)
 		version = protocol_v0;
 
 	/* Without this we cannot rely on waitpid() to tell
@@ -1449,14 +1421,14 @@ struct child_process *git_connect(int fd[2], const char *url,
 	 */
 	signal(SIGCHLD, SIG_DFL);
 
-	protocol = parse_connect_url(url, &hostandport, &path);
-	if ((flags & CONNECT_DIAG_URL) && (protocol != PROTO_SSH)) {
+	scheme = parse_connect_url(url, &hostandport, &path);
+	if ((flags & CONNECT_DIAG_URL) && (scheme != URL_SCHEME_SSH)) {
 		printf("Diag: url=%s\n", url ? url : "NULL");
-		printf("Diag: protocol=%s\n", prot_name(protocol));
+		printf("Diag: protocol=%s\n", url_scheme_name(scheme));
 		printf("Diag: hostandport=%s\n", hostandport ? hostandport : "NULL");
 		printf("Diag: path=%s\n", path ? path : "NULL");
 		conn = NULL;
-	} else if (protocol == PROTO_GIT) {
+	} else if (scheme == URL_SCHEME_GIT) {
 		conn = git_connect_git(fd, hostandport, path, prog, version, flags);
 		conn->trace2_child_class = "transport/git";
 	} else {
@@ -1479,7 +1451,7 @@ struct child_process *git_connect(int fd[2], const char *url,
 
 		conn->use_shell = 1;
 		conn->in = conn->out = -1;
-		if (protocol == PROTO_SSH) {
+		if (scheme == URL_SCHEME_SSH) {
 			char *ssh_host = hostandport;
 			const char *port = NULL;
 			transport_check_allowed("ssh");
@@ -1490,7 +1462,7 @@ struct child_process *git_connect(int fd[2], const char *url,
 
 			if (flags & CONNECT_DIAG_URL) {
 				printf("Diag: url=%s\n", url ? url : "NULL");
-				printf("Diag: protocol=%s\n", prot_name(protocol));
+				printf("Diag: protocol=%s\n", url_scheme_name(scheme));
 				printf("Diag: userandhost=%s\n", ssh_host ? ssh_host : "NULL");
 				printf("Diag: port=%s\n", port ? port : "NONE");
 				printf("Diag: path=%s\n", path ? path : "NULL");
diff --git a/connect.h b/connect.h
index 1645126..aa482a3 100644
--- a/connect.h
+++ b/connect.h
@@ -7,13 +7,17 @@
 #define CONNECT_DIAG_URL      (1u << 1)
 #define CONNECT_IPV4          (1u << 2)
 #define CONNECT_IPV6          (1u << 3)
-struct child_process *git_connect(int fd[2], const char *url, const char *name, const char *prog, int flags);
+enum git_connect_service {
+    GIT_CONNECT_UPLOAD_PACK,
+    GIT_CONNECT_RECEIVE_PACK,
+    GIT_CONNECT_UPLOAD_ARCHIVE,
+};
+struct child_process *git_connect(int fd[2], const char *url, enum git_connect_service, const char *prog, int flags);
 int finish_connect(struct child_process *conn);
 int git_connection_is_socket(struct child_process *conn);
 int server_supports(const char *feature);
 int parse_feature_request(const char *features, const char *feature);
 const char *server_feature_value(const char *feature, size_t *len_ret);
-int url_is_local_not_ssh(const char *url);
 
 struct packet_reader;
 enum protocol_version discover_version(struct packet_reader *reader);
diff --git a/connected.c b/connected.c
index 6718503..7e26976 100644
--- a/connected.c
+++ b/connected.c
@@ -76,6 +76,8 @@ int check_connected(oid_iterate_fn fn, void *cb_data,
 promisor_pack_found:
 			;
 		} while ((oid = fn(cb_data)) != NULL);
+		if (opt->err_fd)
+			close(opt->err_fd);
 		return 0;
 	}
 
diff --git a/contrib/buildsystems/CMakeLists.txt b/contrib/buildsystems/CMakeLists.txt
index d7a087e..a57c4b4 100644
--- a/contrib/buildsystems/CMakeLists.txt
+++ b/contrib/buildsystems/CMakeLists.txt
@@ -255,7 +255,7 @@
 	add_compile_definitions(HAVE_ALLOCA_H NO_POSIX_GOODIES NATIVE_CRLF NO_UNIX_SOCKETS WIN32
 				_CONSOLE DETECT_MSYS_TTY STRIP_EXTENSION=".exe"  NO_SYMLINK_HEAD UNRELIABLE_FSTAT
 				NOGDI OBJECT_CREATION_MODE=1 __USE_MINGW_ANSI_STDIO=0
-				USE_NED_ALLOCATOR OVERRIDE_STRDUP MMAP_PREVENTS_DELETE USE_WIN32_MMAP
+				OVERRIDE_STRDUP MMAP_PREVENTS_DELETE USE_WIN32_MMAP
 				HAVE_WPGMPTR ENSURE_MSYSTEM_IS_SET HAVE_RTLGENRANDOM)
 	list(APPEND compat_SOURCES
 		compat/mingw.c
@@ -267,7 +267,6 @@
 		compat/win32/syslog.c
 		compat/win32/trace2_win32_process_info.c
 		compat/win32/dirent.c
-		compat/nedmalloc/nedmalloc.c
 		compat/strdup.c)
 	set(NO_UNIX_SOCKETS 1)
 
@@ -293,23 +292,26 @@
 
 if(SUPPORTS_SIMPLE_IPC)
 	if(CMAKE_SYSTEM_NAME STREQUAL "Windows")
-		add_compile_definitions(HAVE_FSMONITOR_DAEMON_BACKEND)
-		list(APPEND compat_SOURCES compat/fsmonitor/fsm-listen-win32.c)
-		list(APPEND compat_SOURCES compat/fsmonitor/fsm-health-win32.c)
-		list(APPEND compat_SOURCES compat/fsmonitor/fsm-ipc-win32.c)
-		list(APPEND compat_SOURCES compat/fsmonitor/fsm-path-utils-win32.c)
-
-		add_compile_definitions(HAVE_FSMONITOR_OS_SETTINGS)
-		list(APPEND compat_SOURCES compat/fsmonitor/fsm-settings-win32.c)
+		set(FSMONITOR_DAEMON_BACKEND "win32")
+		set(FSMONITOR_OS_SETTINGS "win32")
 	elseif(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
+		set(FSMONITOR_DAEMON_BACKEND "darwin")
+		set(FSMONITOR_OS_SETTINGS "unix")
+	elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux")
+		set(FSMONITOR_DAEMON_BACKEND "linux")
+		set(FSMONITOR_OS_SETTINGS "unix")
+		add_compile_definitions(HAVE_LINUX_MAGIC_H)
+	endif()
+
+	if(FSMONITOR_DAEMON_BACKEND)
 		add_compile_definitions(HAVE_FSMONITOR_DAEMON_BACKEND)
-		list(APPEND compat_SOURCES compat/fsmonitor/fsm-listen-darwin.c)
-		list(APPEND compat_SOURCES compat/fsmonitor/fsm-health-darwin.c)
-		list(APPEND compat_SOURCES compat/fsmonitor/fsm-ipc-darwin.c)
-		list(APPEND compat_SOURCES compat/fsmonitor/fsm-path-utils-darwin.c)
+		list(APPEND compat_SOURCES compat/fsmonitor/fsm-listen-${FSMONITOR_DAEMON_BACKEND}.c)
+		list(APPEND compat_SOURCES compat/fsmonitor/fsm-health-${FSMONITOR_DAEMON_BACKEND}.c)
+		list(APPEND compat_SOURCES compat/fsmonitor/fsm-ipc-${FSMONITOR_OS_SETTINGS}.c)
+		list(APPEND compat_SOURCES compat/fsmonitor/fsm-path-utils-${FSMONITOR_DAEMON_BACKEND}.c)
 
 		add_compile_definitions(HAVE_FSMONITOR_OS_SETTINGS)
-		list(APPEND compat_SOURCES compat/fsmonitor/fsm-settings-darwin.c)
+		list(APPEND compat_SOURCES compat/fsmonitor/fsm-settings-${FSMONITOR_OS_SETTINGS}.c)
 	endif()
 endif()
 
@@ -376,7 +378,7 @@
 #function checks
 set(function_checks
 	strcasestr memmem strlcpy strtoimax strtoumax strtoull
-	setenv mkdtemp poll pread memmem writev)
+	setenv mkdtemp poll pread memmem)
 
 #unsetenv,hstrerror are incompatible with windows build
 if(NOT WIN32)
@@ -421,10 +423,6 @@
 	list(APPEND compat_SOURCES compat/memmem.c)
 endif()
 
-if(NOT HAVE_WRITEV)
-	list(APPEND compat_SOURCES compat/writev.c)
-endif()
-
 if(NOT WIN32)
 	if(NOT HAVE_UNSETENV)
 		list(APPEND compat_SOURCES compat/unsetenv.c)
@@ -1156,8 +1154,8 @@
 file(STRINGS ${CMAKE_SOURCE_DIR}/GIT-BUILD-OPTIONS.in git_build_options NEWLINE_CONSUME)
 string(REPLACE "@BROKEN_PATH_FIX@" "" git_build_options "${git_build_options}")
 string(REPLACE "@DIFF@" "'${DIFF}'" git_build_options "${git_build_options}")
-string(REPLACE "@FSMONITOR_DAEMON_BACKEND@" "win32" git_build_options "${git_build_options}")
-string(REPLACE "@FSMONITOR_OS_SETTINGS@" "win32" git_build_options "${git_build_options}")
+string(REPLACE "@FSMONITOR_DAEMON_BACKEND@" "${FSMONITOR_DAEMON_BACKEND}" git_build_options "${git_build_options}")
+string(REPLACE "@FSMONITOR_OS_SETTINGS@" "${FSMONITOR_OS_SETTINGS}" git_build_options "${git_build_options}")
 string(REPLACE "@GITWEBDIR@" "'${GITWEBDIR}'" git_build_options "${git_build_options}")
 string(REPLACE "@GIT_INTEROP_MAKE_OPTS@" "" git_build_options "${git_build_options}")
 string(REPLACE "@GIT_PERF_LARGE_REPO@" "" git_build_options "${git_build_options}")
diff --git a/contrib/vscode/init.sh b/contrib/vscode/init.sh
index f2d61bb..3d58f73 100755
--- a/contrib/vscode/init.sh
+++ b/contrib/vscode/init.sh
@@ -202,7 +202,6 @@
         "\\bUSE_STDEV\\b",
         "\\Wchar *\\*\\W*utfs\\W",
         "cURL's",
-        "nedmalloc'ed",
         "ntifs\\.h",
     ],
 }
diff --git a/convert.c b/convert.c
index a34ec6e..eae36c8 100644
--- a/convert.c
+++ b/convert.c
@@ -1168,7 +1168,8 @@ static int ident_to_worktree(const char *src, size_t len,
 			     struct strbuf *buf, int ident)
 {
 	struct object_id oid;
-	char *to_free = NULL, *dollar, *spc;
+	char *to_free = NULL;
+	const char *dollar, *spc;
 	int cnt;
 
 	if (!ident)
diff --git a/csum-file.c b/csum-file.c
index 9558177..d7a682c 100644
--- a/csum-file.c
+++ b/csum-file.c
@@ -178,7 +178,7 @@ struct hashfile *hashfd_ext(const struct git_hash_algo *algop,
 	f->algop = unsafe_hash_algo(algop);
 	f->algop->init_fn(&f->ctx);
 
-	f->buffer_len = opts->buffer_len ? opts->buffer_len : 128 * 1024;
+	f->buffer_len = opts->buffer_len ? opts->buffer_len : DEFAULT_IO_BUFFER_SIZE;
 	f->buffer = xmalloc(f->buffer_len);
 	f->check_buffer = NULL;
 
diff --git a/daemon.c b/daemon.c
index 0a7b1aa..947dd90 100644
--- a/daemon.c
+++ b/daemon.c
@@ -244,14 +244,14 @@ static const char *path_ok(const char *directory, struct hostinfo *hi)
 	}
 
 	enter_repo_flags = strict_paths ? ENTER_REPO_STRICT : 0;
-	path = enter_repo(dir, enter_repo_flags);
+	path = enter_repo(the_repository, dir, enter_repo_flags);
 	if (!path && base_path && base_path_relaxed) {
 		/*
 		 * if we fail and base_path_relaxed is enabled, try without
 		 * prefixing the base path
 		 */
 		dir = directory;
-		path = enter_repo(dir, enter_repo_flags);
+		path = enter_repo(the_repository, dir, enter_repo_flags);
 	}
 
 	if (!path) {
diff --git a/date.c b/date.c
index 17a9507..05b78d8 100644
--- a/date.c
+++ b/date.c
@@ -1071,13 +1071,22 @@ void datestamp(struct strbuf *out)
 /*
  * Relative time update (eg "2 days ago").  If we haven't set the time
  * yet, we need to set it from current time.
+ *
+ * The tm->tm_mday field has an additional logic of using negative values
+ * for date adjustments: -2 means yesterday and -3 the day before that,
+ * and so on.  The idea is to deref such adjustments until we are sure
+ * there's no explicit mday specification in the approxidate string.
  */
 static time_t update_tm(struct tm *tm, struct tm *now, time_t sec)
 {
 	time_t n;
 
-	if (tm->tm_mday < 0)
+	if (tm->tm_mday < 0) {
+		int offset = tm->tm_mday + 1;
+		if (sec == 0 && offset < 0)
+			sec = -offset * 24*60*60;
 		tm->tm_mday = now->tm_mday;
+	}
 	if (tm->tm_mon < 0)
 		tm->tm_mon = now->tm_mon;
 	if (tm->tm_year < 0) {
@@ -1127,34 +1136,39 @@ static void date_now(struct tm *tm, struct tm *now, int *num)
 static void date_yesterday(struct tm *tm, struct tm *now, int *num)
 {
 	*num = 0;
+	tm->tm_mday = -1;
 	update_tm(tm, now, 24*60*60);
 }
 
-static void date_time(struct tm *tm, struct tm *now, int hour)
+static void date_time(struct tm *tm, int hour)
 {
-	if (tm->tm_hour < hour)
-		update_tm(tm, now, 24*60*60);
+	/*
+	 * If we do not yet have a specified day, we'll use the most recent
+	 * version of "hour" relative to now.  But that may be yesterday.
+	 */
+	if (tm->tm_mday < 0 && tm->tm_hour < hour)
+		tm->tm_mday = -2; /* eventually handled by update_tm() */
 	tm->tm_hour = hour;
 	tm->tm_min = 0;
 	tm->tm_sec = 0;
 }
 
-static void date_midnight(struct tm *tm, struct tm *now, int *num)
+static void date_midnight(struct tm *tm, struct tm *now UNUSED, int *num)
 {
 	pending_number(tm, num);
-	date_time(tm, now, 0);
+	date_time(tm, 0);
 }
 
-static void date_noon(struct tm *tm, struct tm *now, int *num)
+static void date_noon(struct tm *tm, struct tm *now UNUSED, int *num)
 {
 	pending_number(tm, num);
-	date_time(tm, now, 12);
+	date_time(tm, 12);
 }
 
-static void date_tea(struct tm *tm, struct tm *now, int *num)
+static void date_tea(struct tm *tm, struct tm *now UNUSED, int *num)
 {
 	pending_number(tm, num);
-	date_time(tm, now, 17);
+	date_time(tm, 17);
 }
 
 static void date_pm(struct tm *tm, struct tm *now UNUSED, int *num)
@@ -1192,6 +1206,17 @@ static void date_never(struct tm *tm, struct tm *now UNUSED, int *num)
 	*num = 0;
 }
 
+static void date_today(struct tm *tm, struct tm *now, int *num)
+{
+	if (tm->tm_hour == now->tm_hour &&
+	    tm->tm_min == now->tm_min &&
+	    tm->tm_sec == now->tm_sec)
+		date_time(tm, 0);
+	*num = 0;
+	tm->tm_mday = -1;
+	update_tm(tm, now, 0);
+}
+
 static const struct special {
 	const char *name;
 	void (*fn)(struct tm *, struct tm *, int *);
@@ -1204,6 +1229,7 @@ static const struct special {
 	{ "AM", date_am },
 	{ "never", date_never },
 	{ "now", date_now },
+	{ "today", date_today },
 	{ NULL }
 };
 
diff --git a/delta.h b/delta.h
index 8a56ec0..fad68cf 100644
--- a/delta.h
+++ b/delta.h
@@ -86,8 +86,11 @@ void *patch_delta(const void *src_buf, unsigned long src_size,
  * This must be called twice on the delta data buffer, first to get the
  * expected source buffer size, and again to get the target buffer size.
  */
-static inline unsigned long get_delta_hdr_size(const unsigned char **datap,
-					       const unsigned char *top)
+/*
+ * Size_t variant that doesn't truncate - use for >4GB objects on Windows.
+ */
+static inline size_t get_delta_hdr_size_sz(const unsigned char **datap,
+					   const unsigned char *top)
 {
 	const unsigned char *data = *datap;
 	size_t cmd, size = 0;
@@ -98,6 +101,13 @@ static inline unsigned long get_delta_hdr_size(const unsigned char **datap,
 		i += 7;
 	} while (cmd & 0x80 && data < top);
 	*datap = data;
+	return size;
+}
+
+static inline unsigned long get_delta_hdr_size(const unsigned char **datap,
+					       const unsigned char *top)
+{
+	size_t size = get_delta_hdr_size_sz(datap, top);
 	return cast_size_t_to_ulong(size);
 }
 
diff --git a/diff.c b/diff.c
index e87847f..5a584fa 100644
--- a/diff.c
+++ b/diff.c
@@ -60,8 +60,8 @@ static int diff_suppress_blank_empty;
 static enum git_colorbool diff_use_color_default = GIT_COLOR_UNKNOWN;
 static int diff_color_moved_default;
 static int diff_color_moved_ws_default;
-static int diff_context_default = 3;
-static int diff_interhunk_context_default;
+static unsigned int diff_context_default = 3;
+static unsigned int diff_interhunk_context_default;
 static char *diff_word_regex_cfg;
 static struct external_diff external_diff_cfg;
 static char *diff_order_file_cfg;
@@ -382,16 +382,17 @@ int git_diff_ui_config(const char *var, const char *value,
 		return 0;
 	}
 	if (!strcmp(var, "diff.context")) {
-		diff_context_default = git_config_int(var, value, ctx->kvi);
-		if (diff_context_default < 0)
+		int val = git_config_int(var, value, ctx->kvi);
+		if (val < 0)
 			return -1;
+		diff_context_default = val;
 		return 0;
 	}
 	if (!strcmp(var, "diff.interhunkcontext")) {
-		diff_interhunk_context_default = git_config_int(var, value,
-								ctx->kvi);
-		if (diff_interhunk_context_default < 0)
+		int val = git_config_int(var, value, ctx->kvi);
+		if (val < 0)
 			return -1;
+		diff_interhunk_context_default = val;
 		return 0;
 	}
 	if (!strcmp(var, "diff.renames")) {
@@ -608,6 +609,52 @@ struct emit_callback {
 	struct strbuf *header;
 };
 
+/*
+ * State for the line-range callback wrappers that sit between
+ * xdi_diff_outf() and fn_out_consume().  xdiff produces a normal,
+ * unfiltered diff; the wrappers intercept each hunk header and line,
+ * track post-image position, and forward only lines that fall within
+ * the requested ranges.  Contiguous in-range lines are collected into
+ * range hunks and flushed with a synthetic @@ header so that
+ * fn_out_consume() sees well-formed unified-diff fragments.
+ *
+ * Removal lines ('-') cannot be classified by post-image position, so
+ * they are buffered in pending_rm until the next '+' or ' ' line
+ * reveals whether they precede an in-range line (flush into range hunk) or
+ * an out-of-range line (discard).
+ */
+struct line_range_callback {
+	xdiff_emit_line_fn orig_line_fn;
+	void *orig_cb_data;
+	const struct range_set *ranges;	/* 0-based [start, end) */
+	unsigned int cur_range;		/* index into the range_set */
+
+	/* Post/pre-image line counters (1-based, set from hunk headers) */
+	long lno_post;
+	long lno_pre;
+
+	/*
+	 * Function name from most recent xdiff hunk header;
+	 * size matches struct func_line.buf in xdiff/xemit.c.
+	 */
+	char func[80];
+	long funclen;
+
+	/* Range hunk being accumulated for the current range */
+	struct strbuf rhunk;
+	long rhunk_old_begin, rhunk_old_count;
+	long rhunk_new_begin, rhunk_new_count;
+	int rhunk_active;
+	int rhunk_has_changes;		/* any '+' or '-' lines? */
+
+	/* Removal lines not yet known to be in-range */
+	struct strbuf pending_rm;
+	int pending_rm_count;
+	long pending_rm_pre_begin;	/* pre-image line of first pending */
+
+	int ret;			/* latched error from orig_line_fn */
+};
+
 static int count_lines(const char *data, int size)
 {
 	int count, ch, completely_empty = 1, nl_just_seen = 0;
@@ -2493,6 +2540,188 @@ static int quick_consume(void *priv, char *line UNUSED, unsigned long len UNUSED
 	return 1;
 }
 
+static void discard_pending_rm(struct line_range_callback *s)
+{
+	strbuf_reset(&s->pending_rm);
+	s->pending_rm_count = 0;
+}
+
+static void flush_rhunk(struct line_range_callback *s)
+{
+	struct strbuf hdr = STRBUF_INIT;
+	const char *p, *end;
+
+	if (!s->rhunk_active || s->ret)
+		return;
+
+	/* Drain any pending removal lines into the range hunk */
+	if (s->pending_rm_count) {
+		strbuf_addbuf(&s->rhunk, &s->pending_rm);
+		s->rhunk_old_count += s->pending_rm_count;
+		s->rhunk_has_changes = 1;
+		discard_pending_rm(s);
+	}
+
+	/*
+	 * Suppress context-only hunks: they contain no actual changes
+	 * and would just be noise.  This can happen when the inflated
+	 * ctxlen causes xdiff to emit context covering a range that
+	 * has no changes in this commit.
+	 */
+	if (!s->rhunk_has_changes) {
+		s->rhunk_active = 0;
+		strbuf_reset(&s->rhunk);
+		return;
+	}
+
+	strbuf_addf(&hdr, "@@ -%ld,%ld +%ld,%ld @@",
+		    s->rhunk_old_begin, s->rhunk_old_count,
+		    s->rhunk_new_begin, s->rhunk_new_count);
+	if (s->funclen > 0) {
+		strbuf_addch(&hdr, ' ');
+		strbuf_add(&hdr, s->func, s->funclen);
+	}
+	strbuf_addch(&hdr, '\n');
+
+	s->ret = s->orig_line_fn(s->orig_cb_data, hdr.buf, hdr.len);
+	strbuf_release(&hdr);
+
+	/*
+	 * Replay buffered lines one at a time through fn_out_consume.
+	 * The cast discards const because xdiff_emit_line_fn takes
+	 * char *, though fn_out_consume does not modify the buffer.
+	 */
+	p = s->rhunk.buf;
+	end = p + s->rhunk.len;
+	while (!s->ret && p < end) {
+		const char *eol = memchr(p, '\n', end - p);
+		unsigned long line_len = eol ? (unsigned long)(eol - p + 1)
+					     : (unsigned long)(end - p);
+		s->ret = s->orig_line_fn(s->orig_cb_data, (char *)p, line_len);
+		p += line_len;
+	}
+
+	s->rhunk_active = 0;
+	strbuf_reset(&s->rhunk);
+}
+
+static void line_range_hunk_fn(void *data,
+			       long old_begin, long old_nr UNUSED,
+			       long new_begin, long new_nr UNUSED,
+			       const char *func, long funclen)
+{
+	struct line_range_callback *s = data;
+
+	/*
+	 * When count > 0, begin is 1-based.  When count == 0, begin is
+	 * adjusted down by 1 by xdl_emit_hunk_hdr(), but no lines of
+	 * that type will arrive, so the value is unused.
+	 *
+	 * Any pending removal lines from the previous xdiff hunk are
+	 * intentionally left in pending_rm: the line callback will
+	 * flush or discard them when the next content line reveals
+	 * whether the removals precede in-range content.
+	 */
+	s->lno_post = new_begin;
+	s->lno_pre = old_begin;
+
+	if (funclen > 0) {
+		if (funclen > (long)sizeof(s->func))
+			funclen = sizeof(s->func);
+		memcpy(s->func, func, funclen);
+	}
+	s->funclen = funclen;
+}
+
+static int line_range_line_fn(void *priv, char *line, unsigned long len)
+{
+	struct line_range_callback *s = priv;
+	const struct range *cur;
+	long lno_0, cur_pre;
+
+	if (s->ret)
+		return s->ret;
+
+	if (line[0] == '-') {
+		if (!s->pending_rm_count)
+			s->pending_rm_pre_begin = s->lno_pre;
+		s->lno_pre++;
+		strbuf_add(&s->pending_rm, line, len);
+		s->pending_rm_count++;
+		return s->ret;
+	}
+
+	if (line[0] == '\\') {
+		if (s->pending_rm_count)
+			strbuf_add(&s->pending_rm, line, len);
+		else if (s->rhunk_active)
+			strbuf_add(&s->rhunk, line, len);
+		/* otherwise outside tracked range; drop silently */
+		return s->ret;
+	}
+
+	if (line[0] != '+' && line[0] != ' ')
+		BUG("unexpected diff line type '%c'", line[0]);
+
+	lno_0 = s->lno_post - 1;
+	cur_pre = s->lno_pre;	/* save before advancing for context lines */
+	s->lno_post++;
+	if (line[0] == ' ')
+		s->lno_pre++;
+
+	/* Advance past ranges we've passed */
+	while (s->cur_range < s->ranges->nr &&
+	       lno_0 >= s->ranges->ranges[s->cur_range].end) {
+		if (s->rhunk_active)
+			flush_rhunk(s);
+		discard_pending_rm(s);
+		s->cur_range++;
+	}
+
+	/* Past all ranges */
+	if (s->cur_range >= s->ranges->nr) {
+		discard_pending_rm(s);
+		return s->ret;
+	}
+
+	cur = &s->ranges->ranges[s->cur_range];
+
+	/* Before current range */
+	if (lno_0 < cur->start) {
+		discard_pending_rm(s);
+		return s->ret;
+	}
+
+	/* In range so start a new range hunk if needed */
+	if (!s->rhunk_active) {
+		s->rhunk_active = 1;
+		s->rhunk_has_changes = 0;
+		s->rhunk_new_begin = lno_0 + 1;
+		s->rhunk_old_begin = s->pending_rm_count
+			? s->pending_rm_pre_begin : cur_pre;
+		s->rhunk_old_count = 0;
+		s->rhunk_new_count = 0;
+		strbuf_reset(&s->rhunk);
+	}
+
+	/* Flush pending removals into range hunk */
+	if (s->pending_rm_count) {
+		strbuf_addbuf(&s->rhunk, &s->pending_rm);
+		s->rhunk_old_count += s->pending_rm_count;
+		s->rhunk_has_changes = 1;
+		discard_pending_rm(s);
+	}
+
+	strbuf_add(&s->rhunk, line, len);
+	s->rhunk_new_count++;
+	if (line[0] == '+')
+		s->rhunk_has_changes = 1;
+	else
+		s->rhunk_old_count++;
+
+	return s->ret;
+}
+
 static void pprint_rename(struct strbuf *name, const char *a, const char *b)
 {
 	const char *old_name = a;
@@ -2699,6 +2928,28 @@ void print_stat_summary(FILE *fp, int files,
 	print_stat_summary_inserts_deletes(&o, files, insertions, deletions);
 }
 
+/*
+ * Like utf8_width(), but guaranteed safe for use in loops that subtract
+ * per-character widths:
+ *
+ *   - utf8_width() sets *start to NULL on invalid UTF-8 and returns 0;
+ *     we restore the pointer and advance by one byte, returning width 1
+ *     (matching the strlen()-based fallback in utf8_strwidth()).
+ *
+ *   - utf8_width() returns -1 for control characters; we return 0
+ *     (matching utf8_strnwidth() which skips them).
+ */
+static int utf8_ish_width(const char **start)
+{
+	const char *old = *start;
+	int w = utf8_width(start, NULL);
+	if (!*start) {
+		*start = old + 1;
+		return 1;
+	}
+	return (w < 0) ? 0 : w;
+}
+
 static void show_stats(struct diffstat_t *data, struct diff_options *options)
 {
 	int i, len, add, del, adds = 0, dels = 0;
@@ -2865,8 +3116,8 @@ static void show_stats(struct diffstat_t *data, struct diff_options *options)
 			if (len < 0)
 				len = 0;
 
-			while (name_len > len)
-				name_len -= utf8_width((const char**)&name, NULL);
+			while (name_len > len && *name)
+				name_len -= utf8_ish_width((const char**)&name);
 
 			slash = strchr(name, '/');
 			if (slash)
@@ -3592,7 +3843,8 @@ static void builtin_diff(const char *name_a,
 			 const char *xfrm_msg,
 			 int must_show_header,
 			 struct diff_options *o,
-			 int complete_rewrite)
+			 int complete_rewrite,
+			 const struct range_set *line_ranges)
 {
 	mmfile_t mf1, mf2;
 	const char *lbl[2];
@@ -3833,6 +4085,52 @@ static void builtin_diff(const char *name_a,
 			 */
 			xdi_diff_outf(&mf1, &mf2, NULL, quick_consume,
 				      &ecbdata, &xpp, &xecfg);
+		} else if (line_ranges) {
+			struct line_range_callback lr_state;
+			unsigned int i;
+			long max_span = 0;
+
+			memset(&lr_state, 0, sizeof(lr_state));
+			lr_state.orig_line_fn = fn_out_consume;
+			lr_state.orig_cb_data = &ecbdata;
+			lr_state.ranges = line_ranges;
+			strbuf_init(&lr_state.rhunk, 0);
+			strbuf_init(&lr_state.pending_rm, 0);
+
+			/*
+			 * Inflate ctxlen so that all changes within
+			 * any single range are merged into one xdiff
+			 * hunk and the inter-change context is emitted.
+			 * The callback clips back to range boundaries.
+			 *
+			 * The optimal ctxlen depends on where changes
+			 * fall within the range, which is only known
+			 * after xdiff runs; the max range span is the
+			 * upper bound that guarantees correctness in a
+			 * single pass.
+			 */
+			for (i = 0; i < line_ranges->nr; i++) {
+				long span = line_ranges->ranges[i].end -
+					    line_ranges->ranges[i].start;
+				if (span > max_span)
+					max_span = span;
+			}
+			if (max_span > xecfg.ctxlen)
+				xecfg.ctxlen = max_span;
+
+			if (xdi_diff_outf(&mf1, &mf2,
+					  line_range_hunk_fn,
+					  line_range_line_fn,
+					  &lr_state, &xpp, &xecfg))
+				die("unable to generate diff for %s",
+				    one->path);
+
+			flush_rhunk(&lr_state);
+			if (lr_state.ret)
+				die("unable to generate diff for %s",
+				    one->path);
+			strbuf_release(&lr_state.rhunk);
+			strbuf_release(&lr_state.pending_rm);
 		} else if (xdi_diff_outf(&mf1, &mf2, NULL, fn_out_consume,
 					 &ecbdata, &xpp, &xecfg))
 			die("unable to generate diff for %s", one->path);
@@ -4674,7 +4972,7 @@ static void run_diff_cmd(const struct external_diff *pgm,
 
 		builtin_diff(name, other ? other : name,
 			     one, two, xfrm_msg, must_show_header,
-			     o, complete_rewrite);
+			     o, complete_rewrite, p->line_ranges);
 		if (p->status == DIFF_STATUS_COPIED ||
 		    p->status == DIFF_STATUS_RENAMED)
 			o->found_changes = 1;
@@ -5649,9 +5947,12 @@ static int diff_opt_unified(const struct option *opt,
 	BUG_ON_OPT_NEG(unset);
 
 	if (arg) {
-		options->context = strtol(arg, &s, 10);
+		long val = strtol(arg, &s, 10);
 		if (*s)
 			return error(_("%s expects a numerical value"), "--unified");
+		if (val < 0)
+			return error(_("%s expects a non-negative integer"), "--unified");
+		options->context = val;
 	}
 	enable_patch_output(&options->output_format);
 
@@ -5836,9 +6137,8 @@ struct option *add_diff_options(const struct option *opts,
 		OPT_CALLBACK_F(0, "default-prefix", options, NULL,
 			       N_("use default prefixes a/ and b/"),
 			       PARSE_OPT_NONEG | PARSE_OPT_NOARG, diff_opt_default_prefix),
-		OPT_INTEGER_F(0, "inter-hunk-context", &options->interhunkcontext,
-			      N_("show context between diff hunks up to the specified number of lines"),
-			      PARSE_OPT_NONEG),
+		OPT_UNSIGNED(0, "inter-hunk-context", &options->interhunkcontext,
+			     N_("show context between diff hunks up to the specified number of lines")),
 		OPT_CALLBACK_F(0, "output-indicator-new",
 			       &options->output_indicators[OUTPUT_INDICATOR_NEW],
 			       N_("<char>"),
diff --git a/diff.h b/diff.h
index 7eb84aa..bb5cdda 100644
--- a/diff.h
+++ b/diff.h
@@ -294,9 +294,9 @@ struct diff_options {
 	enum git_colorbool use_color;
 
 	/* Number of context lines to generate in patch output. */
-	int context;
+	unsigned int context;
 
-	int interhunkcontext;
+	unsigned int interhunkcontext;
 
 	/* Affects the way detection logic for complete rewrites, renames and
 	 * copies.
diff --git a/diffcore.h b/diffcore.h
index 9c0a0e7..d75038d 100644
--- a/diffcore.h
+++ b/diffcore.h
@@ -19,6 +19,17 @@ struct userdiff_driver;
  * in anything else.
  */
 
+/* A range [start, end).  Lines are numbered starting at 0. */
+struct range {
+	long start, end;
+};
+
+/* A set of ranges.  The ranges must always be disjoint and sorted. */
+struct range_set {
+	unsigned int alloc, nr;
+	struct range *ranges;
+};
+
 /* We internally use unsigned short as the score value,
  * and rely on an int capable to hold 32-bits.  -B can take
  * -Bmerge_score/break_score format and the two scores are
@@ -106,6 +117,11 @@ int diff_filespec_is_binary(struct repository *, struct diff_filespec *);
 struct diff_filepair {
 	struct diff_filespec *one;
 	struct diff_filespec *two;
+	/*
+	 * Tracked line ranges for -L filtering; borrowed from
+	 * line_log_data and must not be freed.
+	 */
+	const struct range_set *line_ranges;
 	unsigned short int score;
 	char status; /* M C R A D U etc. (see Documentation/diff-format.adoc or DIFF_STATUS_* in diff.h) */
 	unsigned broken_pair : 1;
diff --git a/dir.c b/dir.c
index fcb8f6d..33c81c2 100644
--- a/dir.c
+++ b/dir.c
@@ -2985,7 +2985,7 @@ static struct untracked_cache_dir *validate_untracked_cache(struct dir_struct *d
 		return NULL;
 
 	/*
-	 * We only support $GIT_DIR/info/exclude and core.excludesfile
+	 * We only support $GIT_COMMON_DIR/info/exclude and core.excludesfile
 	 * as the global ignore rule files. Any other additions
 	 * (e.g. from command line) invalidate the cache. This
 	 * condition also catches running setup_standard_excludes()
@@ -3078,7 +3078,7 @@ static struct untracked_cache_dir *validate_untracked_cache(struct dir_struct *d
 		istate->cache_changed |= UNTRACKED_CHANGED;
 	}
 
-	/* Validate $GIT_DIR/info/exclude and core.excludesfile */
+	/* Validate $GIT_COMMON_DIR/info/exclude and core.excludesfile */
 	root = dir->untracked->root;
 	if (!oideq(&dir->internal.ss_info_exclude.oid,
 		   &dir->untracked->ss_info_exclude.oid)) {
diff --git a/dir.h b/dir.h
index 20d4a07..83e0f64 100644
--- a/dir.h
+++ b/dir.h
@@ -153,7 +153,7 @@ struct oid_stat {
  *   - The list of files and directories of the directory in question
  *   - The $GIT_DIR/index
  *   - dir_struct flags
- *   - The content of $GIT_DIR/info/exclude
+ *   - The content of $GIT_COMMON_DIR/info/exclude
  *   - The content of core.excludesfile
  *   - The content (or the lack) of .gitignore of all parent directories
  *     from $GIT_WORK_TREE
diff --git a/environment.h b/environment.h
index 123a71c..9eb97b3 100644
--- a/environment.h
+++ b/environment.h
@@ -147,8 +147,6 @@ void repo_config_values_init(struct repo_config_values *cfg);
  * Please do not add new global config variables here.
  */
 # ifdef USE_THE_REPOSITORY_VARIABLE
-void setup_git_env(const char *git_dir);
-
 /*
  * Returns true iff we have a configured git repository (either via
  * setup_git_directory, or in the environment via $GIT_DIR).
diff --git a/fetch-negotiator.h b/fetch-negotiator.h
index e348905..6ca422a 100644
--- a/fetch-negotiator.h
+++ b/fetch-negotiator.h
@@ -47,6 +47,15 @@ struct fetch_negotiator {
 	 */
 	int (*ack)(struct fetch_negotiator *, struct commit *);
 
+	/*
+	 * Inform the negotiator that this commit has already been sent as
+	 * a "have" line outside of the negotiator's control. The negotiator
+	 * should avoid outputting it from next() and may use it to optimize
+	 * further negotiation (e.g., by treating it and its ancestors as
+	 * common).
+	 */
+	void (*have_sent)(struct fetch_negotiator *, struct commit *);
+
 	void (*release)(struct fetch_negotiator *);
 
 	/* internal use */
diff --git a/fetch-pack.c b/fetch-pack.c
index a32224e..120e01f 100644
--- a/fetch-pack.c
+++ b/fetch-pack.c
@@ -25,6 +25,7 @@
 #include "oidset.h"
 #include "packfile.h"
 #include "odb.h"
+#include "object-name.h"
 #include "path.h"
 #include "connected.h"
 #include "fetch-negotiator.h"
@@ -51,7 +52,6 @@ static int server_supports_filtering;
 static int advertise_sid;
 static struct shallow_lock shallow_lock;
 static const char *alternate_shallow_file;
-static struct fsck_options fsck_options = FSCK_OPTIONS_MISSING_GITMODULES;
 static struct strbuf fsck_msg_types = STRBUF_INIT;
 static struct string_list uri_protocols = STRING_LIST_INIT_DUP;
 
@@ -145,7 +145,7 @@ static struct commit *deref_without_lazy_fetch(const struct object_id *oid,
 	if (commit) {
 		if (mark_tags_complete_and_check_obj_db) {
 			if (!odb_has_object(the_repository->objects, oid,
-					    HAS_OBJECT_RECHECK_PACKED))
+					    ODB_HAS_OBJECT_RECHECK_PACKED))
 				die_in_commit_graph_only(oid);
 		}
 		return commit;
@@ -291,21 +291,21 @@ static int next_flush(int stateless_rpc, int count)
 }
 
 static void mark_tips(struct fetch_negotiator *negotiator,
-		      const struct oid_array *negotiation_tips)
+		      const struct oid_array *negotiation_restrict_tips)
 {
 	struct refs_for_each_ref_options opts = {
 		.flags = REFS_FOR_EACH_INCLUDE_BROKEN,
 	};
 	int i;
 
-	if (!negotiation_tips) {
+	if (!negotiation_restrict_tips) {
 		refs_for_each_ref_ext(get_main_ref_store(the_repository),
 				      rev_list_insert_ref_oid, negotiator, &opts);
 		return;
 	}
 
-	for (i = 0; i < negotiation_tips->nr; i++)
-		rev_list_insert_ref(negotiator, &negotiation_tips->oid[i]);
+	for (i = 0; i < negotiation_restrict_tips->nr; i++)
+		rev_list_insert_ref(negotiator, &negotiation_restrict_tips->oid[i]);
 	return;
 }
 
@@ -332,6 +332,21 @@ static void send_filter(struct fetch_pack_args *args,
 	}
 }
 
+static void add_oids_to_set(const struct oid_array *array,
+			    struct oidset *set)
+{
+	if (!array)
+		return;
+
+	for (size_t i = 0; i < array->nr; i++) {
+		struct object_id *oid = &array->oid[i];
+		if (!odb_has_object(the_repository->objects, oid, 0))
+			die(_("the object %s does not exist"), oid_to_hex(oid));
+
+		oidset_insert(set, oid);
+	}
+}
+
 static int find_common(struct fetch_negotiator *negotiator,
 		       struct fetch_pack_args *args,
 		       int fd[2], struct object_id *result_oid,
@@ -347,6 +362,7 @@ static int find_common(struct fetch_negotiator *negotiator,
 	struct strbuf req_buf = STRBUF_INIT;
 	size_t state_len = 0;
 	struct packet_reader reader;
+	struct oidset negotiation_include_oids = OIDSET_INIT;
 
 	if (args->stateless_rpc && multi_ack == 1)
 		die(_("the option '%s' requires '%s'"), "--stateless-rpc", "multi_ack_detailed");
@@ -355,7 +371,7 @@ static int find_common(struct fetch_negotiator *negotiator,
 			   PACKET_READ_CHOMP_NEWLINE |
 			   PACKET_READ_DIE_ON_ERR_PACKET);
 
-	mark_tips(negotiator, args->negotiation_tips);
+	mark_tips(negotiator, args->negotiation_restrict_tips);
 	for_each_cached_alternate(negotiator, insert_one_alternate_object);
 
 	fetching = 0;
@@ -474,6 +490,27 @@ static int find_common(struct fetch_negotiator *negotiator,
 	trace2_region_enter("fetch-pack", "negotiation_v0_v1", the_repository);
 	flushes = 0;
 	retval = -1;
+
+	/* Send unconditional haves from --negotiation-include */
+	add_oids_to_set(args->negotiation_include_tips,
+			&negotiation_include_oids);
+	if (oidset_size(&negotiation_include_oids)) {
+		struct oidset_iter iter;
+		oidset_iter_init(&negotiation_include_oids, &iter);
+
+		while ((oid = oidset_iter_next(&iter))) {
+			struct commit *commit;
+			packet_buf_write(&req_buf, "have %s\n",
+					 oid_to_hex(oid));
+			print_verbose(args, "have %s", oid_to_hex(oid));
+			count++;
+
+			commit = lookup_commit(the_repository, oid);
+			if (commit)
+				negotiator->have_sent(negotiator, commit);
+		}
+	}
+
 	while ((oid = negotiator->next(negotiator))) {
 		packet_buf_write(&req_buf, "have %s\n", oid_to_hex(oid));
 		print_verbose(args, "have %s", oid_to_hex(oid));
@@ -584,6 +621,7 @@ static int find_common(struct fetch_negotiator *negotiator,
 		flushes++;
 	}
 	strbuf_release(&req_buf);
+	oidset_clear(&negotiation_include_oids);
 
 	if (!got_ready || !no_done)
 		consume_shallow_list(args, &reader);
@@ -1096,6 +1134,7 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args,
 				 struct shallow_info *si,
 				 struct string_list *pack_lockfiles)
 {
+	struct fsck_options fsck_options = { 0 };
 	struct repository *r = the_repository;
 	struct ref *ref = copy_ref_list(orig_ref);
 	struct object_id oid;
@@ -1224,6 +1263,8 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args,
 		alternate_shallow_file = setup_temporary_shallow(si->shallow);
 	} else
 		alternate_shallow_file = NULL;
+
+	fsck_options_init(&fsck_options, the_repository, FSCK_OPTIONS_MISSING_GITMODULES);
 	if (get_pack(args, fd, pack_lockfiles, NULL, sought, nr_sought,
 		     &fsck_options.gitmodules_found))
 		die(_("git fetch-pack: fetch failed."));
@@ -1231,6 +1272,7 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args,
 		die("fsck failed");
 
  all_done:
+	fsck_options_clear(&fsck_options);
 	if (negotiator)
 		negotiator->release(negotiator);
 	return ref;
@@ -1301,11 +1343,27 @@ static void add_common(struct strbuf *req_buf, struct oidset *common)
 
 static int add_haves(struct fetch_negotiator *negotiator,
 		     struct strbuf *req_buf,
-		     int *haves_to_send)
+		     int *haves_to_send,
+		     struct oidset *negotiation_include_oids)
 {
 	int haves_added = 0;
 	const struct object_id *oid;
 
+	/* Send unconditional haves from --negotiation-include */
+	if (negotiation_include_oids) {
+		struct oidset_iter iter;
+		oidset_iter_init(negotiation_include_oids, &iter);
+
+		while ((oid = oidset_iter_next(&iter))) {
+			struct commit *commit = lookup_commit(the_repository, oid);
+			if (commit) {
+				packet_buf_write(req_buf, "have %s\n",
+						 oid_to_hex(oid));
+				negotiator->have_sent(negotiator, commit);
+			}
+		}
+	}
+
 	while ((oid = negotiator->next(negotiator))) {
 		packet_buf_write(req_buf, "have %s\n", oid_to_hex(oid));
 		if (++haves_added >= *haves_to_send)
@@ -1354,7 +1412,8 @@ static int send_fetch_request(struct fetch_negotiator *negotiator, int fd_out,
 			      struct fetch_pack_args *args,
 			      const struct ref *wants, struct oidset *common,
 			      int *haves_to_send, int *in_vain,
-			      int sideband_all, int seen_ack)
+			      int sideband_all, int seen_ack,
+			      struct oidset *negotiation_include_oids)
 {
 	int haves_added;
 	int done_sent = 0;
@@ -1409,7 +1468,8 @@ static int send_fetch_request(struct fetch_negotiator *negotiator, int fd_out,
 	/* Add all of the common commits we've found in previous rounds */
 	add_common(&req_buf, common);
 
-	haves_added = add_haves(negotiator, &req_buf, haves_to_send);
+	haves_added = add_haves(negotiator, &req_buf, haves_to_send,
+			       negotiation_include_oids);
 	*in_vain += haves_added;
 	trace2_data_intmax("negotiation_v2", the_repository, "haves_added", haves_added);
 	trace2_data_intmax("negotiation_v2", the_repository, "in_vain", *in_vain);
@@ -1650,9 +1710,11 @@ static struct ref *do_fetch_pack_v2(struct fetch_pack_args *args,
 				    struct string_list *pack_lockfiles)
 {
 	struct repository *r = the_repository;
+	struct fsck_options fsck_options;
 	struct ref *ref = copy_ref_list(orig_ref);
 	enum fetch_state state = FETCH_CHECK_LOCAL;
 	struct oidset common = OIDSET_INIT;
+	struct oidset negotiation_include_oids = OIDSET_INIT;
 	struct packet_reader reader;
 	int in_vain = 0, negotiation_started = 0;
 	int negotiation_round = 0;
@@ -1667,6 +1729,8 @@ static struct ref *do_fetch_pack_v2(struct fetch_pack_args *args,
 	struct strvec index_pack_args = STRVEC_INIT;
 	const char *promisor_remote_config;
 
+	fsck_options_init(&fsck_options, the_repository, FSCK_OPTIONS_MISSING_GITMODULES);
+
 	if (server_feature_v2("promisor-remote", &promisor_remote_config))
 		promisor_remote_reply(promisor_remote_config, NULL);
 
@@ -1724,7 +1788,9 @@ static struct ref *do_fetch_pack_v2(struct fetch_pack_args *args,
 			else
 				state = FETCH_SEND_REQUEST;
 
-			mark_tips(negotiator, args->negotiation_tips);
+			mark_tips(negotiator, args->negotiation_restrict_tips);
+			add_oids_to_set(args->negotiation_include_tips,
+					&negotiation_include_oids);
 			for_each_cached_alternate(negotiator,
 						  insert_one_alternate_object);
 			break;
@@ -1743,7 +1809,8 @@ static struct ref *do_fetch_pack_v2(struct fetch_pack_args *args,
 					       &common,
 					       &haves_to_send, &in_vain,
 					       reader.use_sideband,
-					       seen_ack)) {
+					       seen_ack,
+					       &negotiation_include_oids)) {
 				trace2_region_leave_printf("negotiation_v2", "round",
 							   the_repository, "%d",
 							   negotiation_round);
@@ -1878,7 +1945,9 @@ static struct ref *do_fetch_pack_v2(struct fetch_pack_args *args,
 	if (negotiator)
 		negotiator->release(negotiator);
 
+	fsck_options_clear(&fsck_options);
 	oidset_clear(&common);
+	oidset_clear(&negotiation_include_oids);
 	return ref;
 }
 
@@ -2009,7 +2078,7 @@ static void update_shallow(struct fetch_pack_args *args,
 		struct object_id *oid = si->shallow->oid;
 		for (i = 0; i < si->shallow->nr; i++)
 			if (odb_has_object(the_repository->objects, &oid[i],
-					   HAS_OBJECT_RECHECK_PACKED | HAS_OBJECT_FETCH_PROMISOR))
+					   ODB_HAS_OBJECT_RECHECK_PACKED | ODB_HAS_OBJECT_FETCH_PROMISOR))
 				oid_array_append(&extra, &oid[i]);
 		if (extra.nr) {
 			setup_alternate_shallow(&shallow_lock,
@@ -2173,16 +2242,18 @@ static void clear_common_flag(struct oidset *s)
 	}
 }
 
-void negotiate_using_fetch(const struct oid_array *negotiation_tips,
+void negotiate_using_fetch(const struct oid_array *negotiation_restrict_tips,
 			   const struct string_list *server_options,
 			   int stateless_rpc,
 			   int fd[],
-			   struct oidset *acked_commits)
+			   struct oidset *acked_commits,
+			   const struct oid_array *negotiation_include_tips)
 {
 	struct fetch_negotiator negotiator;
 	struct packet_reader reader;
 	struct object_array nt_object_array = OBJECT_ARRAY_INIT;
 	struct strbuf req_buf = STRBUF_INIT;
+	struct oidset negotiation_include_oids = OIDSET_INIT;
 	int haves_to_send = INITIAL_FLUSH;
 	int in_vain = 0;
 	int seen_ack = 0;
@@ -2191,13 +2262,16 @@ void negotiate_using_fetch(const struct oid_array *negotiation_tips,
 	timestamp_t min_generation = GENERATION_NUMBER_INFINITY;
 
 	fetch_negotiator_init(the_repository, &negotiator);
-	mark_tips(&negotiator, negotiation_tips);
+	mark_tips(&negotiator, negotiation_restrict_tips);
+
+	add_oids_to_set(negotiation_include_tips,
+			&negotiation_include_oids);
 
 	packet_reader_init(&reader, fd[0], NULL, 0,
 			   PACKET_READ_CHOMP_NEWLINE |
 			   PACKET_READ_DIE_ON_ERR_PACKET);
 
-	oid_array_for_each((struct oid_array *) negotiation_tips,
+	oid_array_for_each((struct oid_array *) negotiation_restrict_tips,
 			   add_to_object_array,
 			   &nt_object_array);
 
@@ -2217,7 +2291,8 @@ void negotiate_using_fetch(const struct oid_array *negotiation_tips,
 
 		packet_buf_write(&req_buf, "wait-for-done");
 
-		haves_added = add_haves(&negotiator, &req_buf, &haves_to_send);
+		haves_added = add_haves(&negotiator, &req_buf, &haves_to_send,
+				       &negotiation_include_oids);
 		in_vain += haves_added;
 		if (!haves_added || (seen_ack && in_vain >= MAX_IN_VAIN))
 			last_iteration = 1;
@@ -2269,6 +2344,7 @@ void negotiate_using_fetch(const struct oid_array *negotiation_tips,
 
 	clear_common_flag(acked_commits);
 	object_array_clear(&nt_object_array);
+	oidset_clear(&negotiation_include_oids);
 	negotiator.release(&negotiator);
 	strbuf_release(&req_buf);
 }
diff --git a/fetch-pack.h b/fetch-pack.h
index 9d34703..6d0dec7 100644
--- a/fetch-pack.h
+++ b/fetch-pack.h
@@ -19,9 +19,10 @@ struct fetch_pack_args {
 
 	/*
 	 * If not NULL, during packfile negotiation, fetch-pack will send "have"
-	 * lines only with these tips and their ancestors.
+	 * lines for all _include_ tips and then a subset of the _restrict_ tips.
 	 */
-	const struct oid_array *negotiation_tips;
+	const struct oid_array *negotiation_restrict_tips;
+	const struct oid_array *negotiation_include_tips;
 
 	unsigned deepen_relative:1;
 	unsigned quiet:1;
@@ -89,11 +90,12 @@ struct ref *fetch_pack(struct fetch_pack_args *args,
  * In the capability advertisement that has happened prior to invoking this
  * function, the "wait-for-done" capability must be present.
  */
-void negotiate_using_fetch(const struct oid_array *negotiation_tips,
+void negotiate_using_fetch(const struct oid_array *negotiation_restrict_tips,
 			   const struct string_list *server_options,
 			   int stateless_rpc,
 			   int fd[],
-			   struct oidset *acked_commits);
+			   struct oidset *acked_commits,
+			   const struct oid_array *negotiation_include_tips);
 
 /*
  * Print an appropriate error message for each sought ref that wasn't
diff --git a/fsck.c b/fsck.c
index 0f02cf8..b72200c 100644
--- a/fsck.c
+++ b/fsck.c
@@ -1,5 +1,3 @@
-#define USE_THE_REPOSITORY_VARIABLE
-
 #include "git-compat-util.h"
 #include "date.h"
 #include "dir.h"
@@ -207,7 +205,7 @@ void fsck_set_msg_types(struct fsck_options *options, const char *values)
 			if (equal == len)
 				die("skiplist requires a path");
 			oidset_parse_file(&options->skip_oids, buf + equal + 1,
-					  the_repository->hash_algo);
+					  options->repo->hash_algo);
 			buf += len + 1;
 			continue;
 		}
@@ -360,7 +358,7 @@ static int fsck_walk_tree(struct tree *tree, void *data, struct fsck_options *op
 	int res = 0;
 	const char *name;
 
-	if (repo_parse_tree(the_repository, tree))
+	if (repo_parse_tree(options->repo, tree))
 		return -1;
 
 	name = fsck_get_object_name(options, &tree->object.oid);
@@ -375,14 +373,14 @@ static int fsck_walk_tree(struct tree *tree, void *data, struct fsck_options *op
 			continue;
 
 		if (S_ISDIR(entry.mode)) {
-			obj = (struct object *)lookup_tree(the_repository, &entry.oid);
+			obj = (struct object *)lookup_tree(options->repo, &entry.oid);
 			if (name && obj)
 				fsck_put_object_name(options, &entry.oid, "%s%s/",
 						     name, entry.path);
 			result = options->walk(obj, OBJ_TREE, data, options);
 		}
 		else if (S_ISREG(entry.mode) || S_ISLNK(entry.mode)) {
-			obj = (struct object *)lookup_blob(the_repository, &entry.oid);
+			obj = (struct object *)lookup_blob(options->repo, &entry.oid);
 			if (name && obj)
 				fsck_put_object_name(options, &entry.oid, "%s%s",
 						     name, entry.path);
@@ -409,7 +407,7 @@ static int fsck_walk_commit(struct commit *commit, void *data, struct fsck_optio
 	int result;
 	const char *name;
 
-	if (repo_parse_commit(the_repository, commit))
+	if (repo_parse_commit(options->repo, commit))
 		return -1;
 
 	name = fsck_get_object_name(options, &commit->object.oid);
@@ -417,7 +415,7 @@ static int fsck_walk_commit(struct commit *commit, void *data, struct fsck_optio
 		fsck_put_object_name(options, get_commit_tree_oid(commit),
 				     "%s:", name);
 
-	result = options->walk((struct object *) repo_get_commit_tree(the_repository, commit),
+	result = options->walk((struct object *) repo_get_commit_tree(options->repo, commit),
 			       OBJ_TREE, data, options);
 	if (result < 0)
 		return result;
@@ -474,7 +472,7 @@ static int fsck_walk_tag(struct tag *tag, void *data, struct fsck_options *optio
 {
 	const char *name = fsck_get_object_name(options, &tag->object.oid);
 
-	if (parse_tag(the_repository, tag))
+	if (parse_tag(options->repo, tag))
 		return -1;
 	if (name)
 		fsck_put_object_name(options, &tag->tagged->oid, "%s", name);
@@ -487,7 +485,7 @@ int fsck_walk(struct object *obj, void *data, struct fsck_options *options)
 		return -1;
 
 	if (obj->type == OBJ_NONE)
-		parse_object(the_repository, &obj->oid);
+		parse_object(options->repo, &obj->oid);
 
 	switch (obj->type) {
 	case OBJ_BLOB:
@@ -970,14 +968,14 @@ static int fsck_commit(const struct object_id *oid,
 
 	if (buffer >= buffer_end || !skip_prefix(buffer, "tree ", &buffer))
 		return report(options, oid, OBJ_COMMIT, FSCK_MSG_MISSING_TREE, "invalid format - expected 'tree' line");
-	if (parse_oid_hex(buffer, &tree_oid, &p) || *p != '\n') {
+	if (parse_oid_hex_algop(buffer, &tree_oid, &p, options->repo->hash_algo) || *p != '\n') {
 		err = report(options, oid, OBJ_COMMIT, FSCK_MSG_BAD_TREE_SHA1, "invalid 'tree' line format - bad sha1");
 		if (err)
 			return err;
 	}
 	buffer = p + 1;
 	while (buffer < buffer_end && skip_prefix(buffer, "parent ", &buffer)) {
-		if (parse_oid_hex(buffer, &parent_oid, &p) || *p != '\n') {
+		if (parse_oid_hex_algop(buffer, &parent_oid, &p, options->repo->hash_algo) || *p != '\n') {
 			err = report(options, oid, OBJ_COMMIT, FSCK_MSG_BAD_PARENT_SHA1, "invalid 'parent' line format - bad sha1");
 			if (err)
 				return err;
@@ -1044,7 +1042,7 @@ int fsck_tag_standalone(const struct object_id *oid, const char *buffer,
 		ret = report(options, oid, OBJ_TAG, FSCK_MSG_MISSING_OBJECT, "invalid format - expected 'object' line");
 		goto done;
 	}
-	if (parse_oid_hex(buffer, tagged_oid, &p) || *p != '\n') {
+	if (parse_oid_hex_algop(buffer, tagged_oid, &p, options->repo->hash_algo) || *p != '\n') {
 		ret = report(options, oid, OBJ_TAG, FSCK_MSG_BAD_OBJECT_SHA1, "invalid 'object' line format - bad sha1");
 		if (ret)
 			goto done;
@@ -1336,9 +1334,9 @@ static int fsck_blobs(struct oidset *blobs_found, struct oidset *blobs_done,
 		if (oidset_contains(blobs_done, oid))
 			continue;
 
-		buf = odb_read_object(the_repository->objects, oid, &type, &size);
+		buf = odb_read_object(options->repo->objects, oid, &type, &size);
 		if (!buf) {
-			if (is_promisor_object(the_repository, oid))
+			if (is_promisor_object(options->repo, oid))
 				continue;
 			ret |= report(options,
 				      oid, OBJ_BLOB, msg_missing,
@@ -1380,6 +1378,54 @@ bool fsck_has_queued_checks(struct fsck_options *options)
 	       !oidset_equal(&options->gitattributes_found, &options->gitattributes_done);
 }
 
+void fsck_options_init(struct fsck_options *options,
+		       struct repository *repo,
+		       enum fsck_options_type type)
+{
+	static const struct fsck_options defaults[] = {
+		[FSCK_OPTIONS_DEFAULT] = {
+			.skip_oids = OIDSET_INIT,
+			.gitmodules_found = OIDSET_INIT,
+			.gitmodules_done = OIDSET_INIT,
+			.gitattributes_found = OIDSET_INIT,
+			.gitattributes_done = OIDSET_INIT,
+			.error_func = fsck_objects_error_function
+		},
+		[FSCK_OPTIONS_STRICT] = {
+			.strict = 1,
+			.gitmodules_found = OIDSET_INIT,
+			.gitmodules_done = OIDSET_INIT,
+			.gitattributes_found = OIDSET_INIT,
+			.gitattributes_done = OIDSET_INIT,
+			.error_func = fsck_objects_error_function,
+		},
+		[FSCK_OPTIONS_MISSING_GITMODULES] = {
+			.strict = 1,
+			.gitmodules_found = OIDSET_INIT,
+			.gitmodules_done = OIDSET_INIT,
+			.gitattributes_found = OIDSET_INIT,
+			.gitattributes_done = OIDSET_INIT,
+			.error_func = fsck_objects_error_cb_print_missing_gitmodules,
+		},
+		[FSCK_OPTIONS_REFS] = {
+			.error_func = fsck_refs_error_function,
+		},
+	};
+
+	switch (type) {
+	case FSCK_OPTIONS_DEFAULT:
+	case FSCK_OPTIONS_STRICT:
+	case FSCK_OPTIONS_MISSING_GITMODULES:
+	case FSCK_OPTIONS_REFS:
+		memcpy(options, &defaults[type], sizeof(*options));
+		break;
+	default:
+		BUG("unknown fsck options type %d", type);
+	}
+
+	options->repo = repo;
+}
+
 void fsck_options_clear(struct fsck_options *options)
 {
 	free(options->msg_type);
diff --git a/fsck.h b/fsck.h
index 65ecbb7..e77935c 100644
--- a/fsck.h
+++ b/fsck.h
@@ -166,7 +166,10 @@ struct fsck_ref_report {
 	const char *path;
 };
 
+struct repository;
+
 struct fsck_options {
+	struct repository *repo;
 	fsck_walk_func walk;
 	fsck_error error_func;
 	unsigned strict;
@@ -180,34 +183,6 @@ struct fsck_options {
 	kh_oid_map_t *object_names;
 };
 
-#define FSCK_OPTIONS_DEFAULT { \
-	.skip_oids = OIDSET_INIT, \
-	.gitmodules_found = OIDSET_INIT, \
-	.gitmodules_done = OIDSET_INIT, \
-	.gitattributes_found = OIDSET_INIT, \
-	.gitattributes_done = OIDSET_INIT, \
-	.error_func = fsck_objects_error_function \
-}
-#define FSCK_OPTIONS_STRICT { \
-	.strict = 1, \
-	.gitmodules_found = OIDSET_INIT, \
-	.gitmodules_done = OIDSET_INIT, \
-	.gitattributes_found = OIDSET_INIT, \
-	.gitattributes_done = OIDSET_INIT, \
-	.error_func = fsck_objects_error_function, \
-}
-#define FSCK_OPTIONS_MISSING_GITMODULES { \
-	.strict = 1, \
-	.gitmodules_found = OIDSET_INIT, \
-	.gitmodules_done = OIDSET_INIT, \
-	.gitattributes_found = OIDSET_INIT, \
-	.gitattributes_done = OIDSET_INIT, \
-	.error_func = fsck_objects_error_cb_print_missing_gitmodules, \
-}
-#define FSCK_REFS_OPTIONS_DEFAULT { \
-	.error_func = fsck_refs_error_function, \
-}
-
 /* descend in all linked child objects
  * the return value is:
  *    -1	error in processing the object
@@ -255,6 +230,17 @@ int fsck_finish(struct fsck_options *options);
  */
 bool fsck_has_queued_checks(struct fsck_options *options);
 
+enum fsck_options_type {
+	FSCK_OPTIONS_DEFAULT,
+	FSCK_OPTIONS_STRICT,
+	FSCK_OPTIONS_MISSING_GITMODULES,
+	FSCK_OPTIONS_REFS,
+};
+
+void fsck_options_init(struct fsck_options *options,
+		       struct repository *repo,
+		       enum fsck_options_type type);
+
 /*
  * Clear the fsck_options struct, freeing any allocated memory.
  */
diff --git a/fsmonitor-ipc.c b/fsmonitor-ipc.c
index f1b1631..6112d13 100644
--- a/fsmonitor-ipc.c
+++ b/fsmonitor-ipc.c
@@ -61,6 +61,9 @@ static int spawn_daemon(void)
 
 	cmd.git_cmd = 1;
 	cmd.no_stdin = 1;
+	cmd.no_stdout = 1;
+	cmd.no_stderr = 1;
+	cmd.close_fd_above_stderr = 1;
 	cmd.trace2_child_class = "fsmonitor";
 	strvec_pushl(&cmd.args, "fsmonitor--daemon", "start", NULL);
 
diff --git a/git-compat-util.h b/git-compat-util.h
index 4b4ea24..8809776 100644
--- a/git-compat-util.h
+++ b/git-compat-util.h
@@ -335,11 +335,7 @@ static inline int is_path_owned_by_current_uid(const char *path,
 #endif
 
 #ifndef find_last_dir_sep
-static inline char *git_find_last_dir_sep(const char *path)
-{
-	return strrchr(path, '/');
-}
-#define find_last_dir_sep git_find_last_dir_sep
+#define find_last_dir_sep(path) strrchr((path), '/')
 #endif
 
 #ifndef has_dir_sep
@@ -468,6 +464,21 @@ report_fn get_warn_routine(void);
 void set_die_is_recursing_routine(int (*routine)(void));
 
 /*
+ * Check that an out-parameter is "at least as const as" a matching
+ * in-parameter. For example, skip_prefix() will return "out" that is a subset
+ * of "str". So:
+ *
+ *  const str, const out: ok
+ *  non-const str, const out: ok
+ *  non-const str, non-const out: ok
+ *  const str, non-const out: compile error
+ *
+ *  See the skip_prefix macro below for an example of use.
+ */
+#define CONST_OUTPARAM(in, out) \
+	((const char **)(0 ? ((*(out) = (in)),(out)) : (out)))
+
+/*
  * If the string "str" begins with the string found in "prefix", return true.
  * The "out" parameter is set to "str + strlen(prefix)" (i.e., to the point in
  * the string right after the prefix).
@@ -483,8 +494,10 @@ void set_die_is_recursing_routine(int (*routine)(void));
  *   [skip prefix if present, otherwise use whole string]
  *   skip_prefix(name, "refs/heads/", &name);
  */
-static inline bool skip_prefix(const char *str, const char *prefix,
-			       const char **out)
+#define skip_prefix(str, prefix, out) \
+	skip_prefix_impl((str), (prefix), CONST_OUTPARAM((str), (out)))
+static inline bool skip_prefix_impl(const char *str, const char *prefix,
+				    const char **out)
 {
 	do {
 		if (!*prefix) {
@@ -601,12 +614,30 @@ static inline bool strip_suffix(const char *str, const char *suffix,
 int git_open_cloexec(const char *name, int flags);
 #define git_open(name) git_open_cloexec(name, O_RDONLY)
 
-static inline size_t st_add(size_t a, size_t b)
+
+/*
+ * Help Clang; GCC generates the same instructions for both variants on
+ * x64 and aarch64.
+ */
+#ifdef __clang__
+#define st_add_overflow __builtin_add_overflow
+#else
+static inline bool st_add_overflow(size_t a, size_t b, size_t *out)
 {
 	if (unsigned_add_overflows(a, b))
+		return true;
+	*out = a + b;
+	return false;
+}
+#endif
+
+static inline size_t st_add(size_t a, size_t b)
+{
+	size_t result;
+	if (st_add_overflow(a, b, &result))
 		die("size_t overflow: %"PRIuMAX" + %"PRIuMAX,
 		    (uintmax_t)a, (uintmax_t)b);
-	return a + b;
+	return result;
 }
 #define st_add3(a,b,c)   st_add(st_add((a),(b)),(c))
 #define st_add4(a,b,c,d) st_add(st_add3((a),(b),(c)),(d))
@@ -699,6 +730,12 @@ static inline uint64_t u64_add(uint64_t a, uint64_t b)
 # endif
 #endif
 
+/*
+ * Default buffer size for buffered I/O in index-pack, unpack-objects,
+ * and the hashfile layer in csum-file.
+ */
+#define DEFAULT_IO_BUFFER_SIZE (128 * 1024)
+
 #ifdef HAVE_ALLOCA_H
 # include <alloca.h>
 # define xalloca(size)      (alloca(size))
@@ -889,8 +926,10 @@ static inline size_t xsize_t(off_t len)
  * is done via tolower(), so it is strictly ASCII (no multi-byte characters or
  * locale-specific conversions).
  */
-static inline bool skip_iprefix(const char *str, const char *prefix,
-			       const char **out)
+#define skip_iprefix(str, prefix, out) \
+	skip_iprefix_impl((str), (prefix), CONST_OUTPARAM((str), (out)))
+static inline bool skip_iprefix_impl(const char *str, const char *prefix,
+				     const char **out)
 {
 	do {
 		if (!*prefix) {
diff --git a/git-zlib.c b/git-zlib.c
index df96049..b91cb32 100644
--- a/git-zlib.c
+++ b/git-zlib.c
@@ -30,6 +30,9 @@ static const char *zerr_to_string(int status)
  */
 /* #define ZLIB_BUF_MAX ((uInt)-1) */
 #define ZLIB_BUF_MAX ((uInt) 1024 * 1024 * 1024) /* 1GB */
+
+/* uLong is 32-bit on Windows, even on 64-bit systems */
+#define ULONG_MAX_VALUE maximum_unsigned_value_of_type(uLong)
 static inline uInt zlib_buf_cap(unsigned long len)
 {
 	return (ZLIB_BUF_MAX < len) ? ZLIB_BUF_MAX : len;
@@ -39,31 +42,37 @@ static void zlib_pre_call(git_zstream *s)
 {
 	s->z.next_in = s->next_in;
 	s->z.next_out = s->next_out;
-	s->z.total_in = s->total_in;
-	s->z.total_out = s->total_out;
+	s->z.total_in = (uLong)(s->total_in & ULONG_MAX_VALUE);
+	s->z.total_out = (uLong)(s->total_out & ULONG_MAX_VALUE);
 	s->z.avail_in = zlib_buf_cap(s->avail_in);
 	s->z.avail_out = zlib_buf_cap(s->avail_out);
 }
 
 static void zlib_post_call(git_zstream *s, int status)
 {
-	unsigned long bytes_consumed;
-	unsigned long bytes_produced;
+	size_t bytes_consumed;
+	size_t bytes_produced;
 
 	bytes_consumed = s->z.next_in - s->next_in;
 	bytes_produced = s->z.next_out - s->next_out;
-	if (s->z.total_out != s->total_out + bytes_produced)
+	/*
+	 * zlib's total_out/total_in are uLong which may wrap for >4GB.
+	 * We track our own totals and verify only the low bits match.
+	 */
+	if ((s->z.total_out & ULONG_MAX_VALUE) !=
+	    ((s->total_out + bytes_produced) & ULONG_MAX_VALUE))
 		BUG("total_out mismatch");
 	/*
 	 * zlib does not update total_in when it returns Z_NEED_DICT,
 	 * causing a mismatch here. Skip the sanity check in that case.
 	 */
 	if (status != Z_NEED_DICT &&
-	    s->z.total_in != s->total_in + bytes_consumed)
+	    (s->z.total_in & ULONG_MAX_VALUE) !=
+	    ((s->total_in + bytes_consumed) & ULONG_MAX_VALUE))
 		BUG("total_in mismatch");
 
-	s->total_out = s->z.total_out;
-	s->total_in = s->z.total_in;
+	s->total_out += bytes_produced;
+	s->total_in += bytes_consumed;
 	/* zlib-ng marks `next_in` as `const`, so we have to cast it away. */
 	s->next_in = (unsigned char *) s->z.next_in;
 	s->next_out = s->z.next_out;
diff --git a/git-zlib.h b/git-zlib.h
index 0e66fef..44380e8 100644
--- a/git-zlib.h
+++ b/git-zlib.h
@@ -7,8 +7,8 @@ typedef struct git_zstream {
 	struct z_stream_s z;
 	unsigned long avail_in;
 	unsigned long avail_out;
-	unsigned long total_in;
-	unsigned long total_out;
+	size_t total_in;
+	size_t total_out;
 	unsigned char *next_in;
 	unsigned char *next_out;
 } git_zstream;
diff --git a/git.c b/git.c
index 5a40eab..36f0889 100644
--- a/git.c
+++ b/git.c
@@ -84,7 +84,7 @@ static int list_cmds(const char *spec)
 	* Set up the repository so we can pick up any repo-level config (like
 	* completion.commands).
 	*/
-	setup_git_directory_gently(&nongit);
+	setup_git_directory_gently(the_repository, &nongit);
 
 	while (*spec) {
 		const char *sep = strchrnul(spec, ',');
@@ -386,7 +386,7 @@ static int handle_alias(struct strvec *args, struct string_list *expanded_aliase
 			int nongit_ok;
 
 			/* Aliases expect GIT_PREFIX, GIT_DIR etc to be set */
-			setup_git_directory_gently(&nongit_ok);
+			setup_git_directory_gently(the_repository, &nongit_ok);
 
 			commit_pager_choice();
 
@@ -477,10 +477,10 @@ static int run_builtin(struct cmd_struct *p, int argc, const char **argv, struct
 		run_setup = RUN_SETUP_GENTLY;
 
 	if (run_setup & RUN_SETUP) {
-		prefix = setup_git_directory();
+		prefix = setup_git_directory(the_repository);
 		no_repo = 0;
 	} else if (run_setup & RUN_SETUP_GENTLY) {
-		prefix = setup_git_directory_gently(&no_repo);
+		prefix = setup_git_directory_gently(the_repository, &no_repo);
 	} else {
 		prefix = NULL;
 	}
@@ -497,7 +497,7 @@ static int run_builtin(struct cmd_struct *p, int argc, const char **argv, struct
 	commit_pager_choice();
 
 	if (!help && p->option & NEED_WORK_TREE)
-		setup_work_tree();
+		setup_work_tree(the_repository);
 
 	trace_argv_printf(argv, "trace: built-in: git");
 	trace2_cmd_name(p->cmd);
@@ -578,6 +578,7 @@ static struct cmd_struct commands[] = {
 	{ "for-each-ref", cmd_for_each_ref, RUN_SETUP },
 	{ "for-each-repo", cmd_for_each_repo, RUN_SETUP_GENTLY },
 	{ "format-patch", cmd_format_patch, RUN_SETUP },
+	{ "format-rev", cmd_format_rev, RUN_SETUP },
 	{ "fsck", cmd_fsck, RUN_SETUP },
 	{ "fsck-objects", cmd_fsck, RUN_SETUP },
 	{ "fsmonitor--daemon", cmd_fsmonitor__daemon, RUN_SETUP },
@@ -670,6 +671,7 @@ static struct cmd_struct commands[] = {
 	{ "upload-archive", cmd_upload_archive, NO_PARSEOPT },
 	{ "upload-archive--writer", cmd_upload_archive_writer, NO_PARSEOPT },
 	{ "upload-pack", cmd_upload_pack },
+	{ "url-parse", cmd_url_parse },
 	{ "var", cmd_var, RUN_SETUP_GENTLY | NO_PARSEOPT },
 	{ "verify-commit", cmd_verify_commit, RUN_SETUP },
 	{ "verify-pack", cmd_verify_pack },
diff --git a/gpg-interface.c b/gpg-interface.c
index d517425..dafd537 100644
--- a/gpg-interface.c
+++ b/gpg-interface.c
@@ -1164,6 +1164,8 @@ int parse_sign_mode(const char *arg, enum sign_mode *mode, const char **keyid)
 		*mode = SIGN_WARN_STRIP;
 	} else if (!strcmp(arg, "strip")) {
 		*mode = SIGN_STRIP;
+	} else if (!strcmp(arg, "abort-if-invalid")) {
+		*mode = SIGN_ABORT_IF_INVALID;
 	} else if (!strcmp(arg, "strip-if-invalid")) {
 		*mode = SIGN_STRIP_IF_INVALID;
 	} else if (!strcmp(arg, "sign-if-invalid")) {
diff --git a/gpg-interface.h b/gpg-interface.h
index a365586..3d95f5e 100644
--- a/gpg-interface.h
+++ b/gpg-interface.h
@@ -115,6 +115,7 @@ void print_signature_buffer(const struct signature_check *sigc,
 /* Modes for --signed-tags=<mode> and --signed-commits=<mode> options. */
 enum sign_mode {
 	SIGN_ABORT,
+	SIGN_ABORT_IF_INVALID,
 	SIGN_WARN_VERBATIM,
 	SIGN_VERBATIM,
 	SIGN_WARN_STRIP,
diff --git a/graph.c b/graph.c
index 26f6fbf..8422826 100644
--- a/graph.c
+++ b/graph.c
@@ -317,6 +317,15 @@ struct git_graph {
 	struct strbuf prefix_buf;
 };
 
+static inline int graph_needs_truncation(struct git_graph *graph, int lane)
+{
+	int max = graph->revs->graph_max_lanes;
+	/*
+	 * Ignore values <= 0, meaning no limit.
+	 */
+	return max > 0 && lane >= max;
+}
+
 static const char *diff_output_prefix_callback(struct diff_options *opt, void *data)
 {
 	struct git_graph *graph = data;
@@ -697,6 +706,19 @@ static void graph_update_columns(struct git_graph *graph)
 	}
 
 	/*
+	 * If graph_max_lanes is set, cap the width
+	 */
+	if (graph->revs->graph_max_lanes > 0) {
+		/*
+		 * width of "| " per lanes plus truncation mark "~ ".
+		 * Allow commits from merges to align to the merged lane.
+		 */
+		int max_width = graph->revs->graph_max_lanes * 2 + 2;
+		if (graph->width > max_width)
+			graph->width = max_width;
+	}
+
+	/*
 	 * Shrink mapping_size to be the minimum necessary
 	 */
 	while (graph->mapping_size > 1 &&
@@ -846,6 +868,10 @@ static void graph_output_padding_line(struct git_graph *graph,
 	 * Output a padding row, that leaves all branch lines unchanged
 	 */
 	for (i = 0; i < graph->num_new_columns; i++) {
+		if (graph_needs_truncation(graph, i)) {
+			graph_line_addstr(line, "~ ");
+			break;
+		}
 		graph_line_write_column(line, &graph->new_columns[i], '|');
 		graph_line_addch(line, ' ');
 	}
@@ -903,6 +929,9 @@ static void graph_output_pre_commit_line(struct git_graph *graph,
 			seen_this = 1;
 			graph_line_write_column(line, col, '|');
 			graph_line_addchars(line, ' ', graph->expansion_row);
+		} else if (seen_this && graph_needs_truncation(graph, i)) {
+			graph_line_addstr(line, "~ ");
+			break;
 		} else if (seen_this && (graph->expansion_row == 0)) {
 			/*
 			 * This is the first line of the pre-commit output.
@@ -994,6 +1023,16 @@ static void graph_draw_octopus_merge(struct git_graph *graph, struct graph_line
 		col = &graph->new_columns[j];
 
 		graph_line_write_column(line, col, '-');
+
+		/*
+		 * Commit is at commit_index, each iteration move one lane to
+		 * the right from the commit.
+		 */
+		if (graph_needs_truncation(graph, graph->commit_index + 1 + i)) {
+			graph_line_addstr(line, "~ ");
+			break;
+		}
+
 		graph_line_write_column(line, col, (i == dashed_parents - 1) ? '.' : '-');
 	}
 
@@ -1028,8 +1067,17 @@ static void graph_output_commit_line(struct git_graph *graph, struct graph_line
 			seen_this = 1;
 			graph_output_commit_char(graph, line);
 
+			if (graph_needs_truncation(graph, i)) {
+				graph_line_addch(line, ' ');
+				break;
+			}
+
 			if (graph->num_parents > 2)
 				graph_draw_octopus_merge(graph, line);
+		} else if (graph_needs_truncation(graph, i)) {
+			graph_line_addstr(line, "~ ");
+			seen_this = 1;
+			break;
 		} else if (seen_this && (graph->edges_added > 1)) {
 			graph_line_write_column(line, col, '\\');
 		} else if (seen_this && (graph->edges_added == 1)) {
@@ -1065,13 +1113,46 @@ static void graph_output_commit_line(struct git_graph *graph, struct graph_line
 
 	/*
 	 * Update graph->state
+	 *
+	 * If the commit is a merge and the first parent is in a visible lane,
+	 * then the GRAPH_POST_MERGE is needed to draw the merge lane.
+	 *
+	 * If the commit is over the truncation limit, but the first parent is on
+	 * a visible lane, then we still need the merge lane but truncated.
+	 *
+	 * If both commit and first parent are over the truncation limit, then
+	 * there's no need to draw the merge lane because it would work as a
+	 * padding lane.
 	 */
-	if (graph->num_parents > 1)
-		graph_update_state(graph, GRAPH_POST_MERGE);
-	else if (graph_is_mapping_correct(graph))
+	if (graph->num_parents > 1) {
+		if (!graph_needs_truncation(graph, graph->commit_index)) {
+			graph_update_state(graph, GRAPH_POST_MERGE);
+		} else {
+			struct commit_list *p = first_interesting_parent(graph);
+			int lane;
+
+			/*
+			 * graph->num_parents are found using first_interesting_parent
+			 * and next_interesting_parent so it can't be a scenario
+			 * where num_parents > 1 and there are no interesting parents
+			 */
+			if (!p)
+				BUG("num_parents > 1 but no interesting parent");
+
+			lane = graph_find_new_column_by_commit(graph, p->item);
+
+			if (!graph_needs_truncation(graph, lane))
+				graph_update_state(graph, GRAPH_POST_MERGE);
+			else if (graph_is_mapping_correct(graph))
+				graph_update_state(graph, GRAPH_PADDING);
+			else
+				graph_update_state(graph, GRAPH_COLLAPSING);
+		}
+	} else if (graph_is_mapping_correct(graph)) {
 		graph_update_state(graph, GRAPH_PADDING);
-	else
+	} else {
 		graph_update_state(graph, GRAPH_COLLAPSING);
+	}
 }
 
 static const char merge_chars[] = {'/', '|', '\\'};
@@ -1109,6 +1190,7 @@ static void graph_output_post_merge_line(struct git_graph *graph, struct graph_l
 			int par_column;
 			int idx = graph->merge_layout;
 			char c;
+			int truncated = 0;
 			seen_this = 1;
 
 			for (j = 0; j < graph->num_parents; j++) {
@@ -1117,23 +1199,56 @@ static void graph_output_post_merge_line(struct git_graph *graph, struct graph_l
 
 				c = merge_chars[idx];
 				graph_line_write_column(line, &graph->new_columns[par_column], c);
+
+				/*
+				 * j counts parents, it needs to be halved to be
+				 * comparable with i. Don't truncate if there are
+				 * no more lanes to print (end of the lane)
+				 */
+				if (graph_needs_truncation(graph, j / 2 + i) &&
+				    j / 2 + i <= graph->num_columns) {
+					if ((j + i * 2) % 2 != 0)
+						graph_line_addch(line, ' ');
+					graph_line_addstr(line, "~ ");
+					truncated = 1;
+					break;
+				}
+
 				if (idx == 2) {
-					if (graph->edges_added > 0 || j < graph->num_parents - 1)
+					/*
+					 * Check if the next lane needs truncation
+					 * to avoid having the padding doubled
+					 */
+					if (graph_needs_truncation(graph, (j + 1) / 2 + i) &&
+					    j < graph->num_parents - 1) {
+						graph_line_addstr(line, "~ ");
+						truncated = 1;
+						break;
+					} else if (graph->edges_added > 0 || j < graph->num_parents - 1)
 						graph_line_addch(line, ' ');
 				} else {
 					idx++;
 				}
 				parents = next_interesting_parent(graph, parents);
 			}
+			if (truncated)
+				break;
 			if (graph->edges_added == 0)
 				graph_line_addch(line, ' ');
-
+		} else if (graph_needs_truncation(graph, i)) {
+			graph_line_addstr(line, "~ ");
+			break;
 		} else if (seen_this) {
 			if (graph->edges_added > 0)
 				graph_line_write_column(line, col, '\\');
 			else
 				graph_line_write_column(line, col, '|');
-			graph_line_addch(line, ' ');
+			/*
+			 * If it's between two lanes and next would be truncated,
+			 * don't add space padding.
+			 */
+			if (!graph_needs_truncation(graph, i + 1))
+				graph_line_addch(line, ' ');
 		} else {
 			graph_line_write_column(line, col, '|');
 			if (graph->merge_layout != 0 || i != graph->commit_index - 1) {
@@ -1164,6 +1279,7 @@ static void graph_output_collapsing_line(struct git_graph *graph, struct graph_l
 	short used_horizontal = 0;
 	int horizontal_edge = -1;
 	int horizontal_edge_target = -1;
+	int truncated = 0;
 
 	/*
 	 * Swap the mapping and old_mapping arrays
@@ -1279,26 +1395,35 @@ static void graph_output_collapsing_line(struct git_graph *graph, struct graph_l
 	 */
 	for (i = 0; i < graph->mapping_size; i++) {
 		int target = graph->mapping[i];
-		if (target < 0)
-			graph_line_addch(line, ' ');
-		else if (target * 2 == i)
-			graph_line_write_column(line, &graph->new_columns[target], '|');
-		else if (target == horizontal_edge_target &&
-			 i != horizontal_edge - 1) {
-				/*
-				 * Set the mappings for all but the
-				 * first segment to -1 so that they
-				 * won't continue into the next line.
-				 */
-				if (i != (target * 2)+3)
-					graph->mapping[i] = -1;
-				used_horizontal = 1;
-			graph_line_write_column(line, &graph->new_columns[target], '_');
+
+		if (!truncated && graph_needs_truncation(graph, i / 2)) {
+			graph_line_addstr(line, "~ ");
+			truncated = 1;
+		}
+
+		if (target < 0) {
+			if (!truncated)
+				graph_line_addch(line, ' ');
+		} else if (target * 2 == i) {
+			if (!truncated)
+				graph_line_write_column(line, &graph->new_columns[target], '|');
+		} else if (target == horizontal_edge_target &&
+			   i != horizontal_edge - 1) {
+			/*
+			 * Set the mappings for all but the
+			 * first segment to -1 so that they
+			 * won't continue into the next line.
+			 */
+			if (i != (target * 2)+3)
+				graph->mapping[i] = -1;
+			used_horizontal = 1;
+			if (!truncated)
+				graph_line_write_column(line, &graph->new_columns[target], '_');
 		} else {
 			if (used_horizontal && i < horizontal_edge)
 				graph->mapping[i] = -1;
-			graph_line_write_column(line, &graph->new_columns[target], '/');
-
+			if (!truncated)
+				graph_line_write_column(line, &graph->new_columns[target], '/');
 		}
 	}
 
@@ -1372,6 +1497,11 @@ static void graph_padding_line(struct git_graph *graph, struct strbuf *sb)
 	for (i = 0; i < graph->num_columns; i++) {
 		struct column *col = &graph->columns[i];
 
+		if (graph_needs_truncation(graph, i)) {
+			graph_line_addstr(&line, "~ ");
+			break;
+		}
+
 		graph_line_write_column(&line, col, '|');
 
 		if (col->commit == graph->commit && graph->num_parents > 2) {
diff --git a/grep.c b/grep.c
index c7e1dc1..a54e5d8 100644
--- a/grep.c
+++ b/grep.c
@@ -1267,6 +1267,7 @@ static void show_line(struct grep_opt *opt,
 		regmatch_t match;
 		enum grep_context ctx = GREP_CONTEXT_BODY;
 		int eflags = 0;
+		const char *start = bol;
 
 		if (want_color(opt->color)) {
 			if (sign == ':')
@@ -1285,6 +1286,7 @@ static void show_line(struct grep_opt *opt,
 			if (match.rm_so == match.rm_eo)
 				break;
 
+			cno = bol - start + match.rm_so + 1;
 			if (opt->only_matching)
 				show_line_header(opt, name, lno, cno, sign);
 			else
@@ -1294,7 +1296,6 @@ static void show_line(struct grep_opt *opt,
 			if (opt->only_matching)
 				opt->output(opt, "\n", 1);
 			bol += match.rm_eo;
-			cno += match.rm_eo;
 			rest -= match.rm_eo;
 			eflags = REG_NOTBOL;
 		}
diff --git a/hash.c b/hash.c
index 553f200..e925b97 100644
--- a/hash.c
+++ b/hash.c
@@ -317,3 +317,21 @@ const struct git_hash_algo *unsafe_hash_algo(const struct git_hash_algo *algop)
 	/* Otherwise use the default one. */
 	return algop;
 }
+
+unsigned oid_common_prefix_hexlen(const struct object_id *a,
+				  const struct object_id *b)
+{
+	unsigned rawsz = hash_algos[a->algo].rawsz;
+
+	for (unsigned i = 0; i < rawsz; i++) {
+		if (a->hash[i] == b->hash[i])
+			continue;
+
+		if ((a->hash[i] ^ b->hash[i]) & 0xf0)
+			return i * 2;
+		else
+			return i * 2 + 1;
+	}
+
+	return rawsz * 2;
+}
diff --git a/hash.h b/hash.h
index d51efce..c082a53 100644
--- a/hash.h
+++ b/hash.h
@@ -396,6 +396,9 @@ static inline int oideq(const struct object_id *oid1, const struct object_id *oi
 	return !memcmp(oid1->hash, oid2->hash, GIT_MAX_RAWSZ);
 }
 
+unsigned oid_common_prefix_hexlen(const struct object_id *a,
+				  const struct object_id *b);
+
 static inline void oidcpy(struct object_id *dst, const struct object_id *src)
 {
 	memcpy(dst->hash, src->hash, GIT_MAX_RAWSZ);
diff --git a/help.c b/help.c
index 3e59d07..4624149 100644
--- a/help.c
+++ b/help.c
@@ -592,14 +592,21 @@ static int git_unknown_cmd_config(const char *var, const char *value,
 	/* Also use aliases for command lookup */
 	if (!parse_config_key(var, "alias", &subsection, &subsection_len,
 			      &key)) {
+		size_t key_len = strlen(key);
+
 		if (subsection) {
 			/* [alias "name"] command = value */
 			if (!strcmp(key, "command"))
 				add_cmdname(&cfg->aliases, subsection,
 					    subsection_len);
+			else {
+				key = var + strlen("alias.");
+				key_len = strlen(key);
+				add_cmdname(&cfg->aliases, key, key_len);
+			}
 		} else {
 			/* alias.name = value */
-			add_cmdname(&cfg->aliases, key, strlen(key));
+			add_cmdname(&cfg->aliases, key, key_len);
 		}
 	}
 
diff --git a/hex.c b/hex.c
index 865a232..bc75672 100644
--- a/hex.c
+++ b/hex.c
@@ -54,9 +54,9 @@ int get_oid_hex(const char *hex, struct object_id *oid)
 	return get_oid_hex_algop(hex, oid, the_hash_algo);
 }
 
-int parse_oid_hex_algop(const char *hex, struct object_id *oid,
-			const char **end,
-			const struct git_hash_algo *algop)
+int parse_oid_hex_algop_impl(const char *hex, struct object_id *oid,
+			     const char **end,
+			     const struct git_hash_algo *algop)
 {
 	int ret = get_oid_hex_algop(hex, oid, algop);
 	if (!ret)
diff --git a/hex.h b/hex.h
index e9ccb54..1e9a65d 100644
--- a/hex.h
+++ b/hex.h
@@ -40,8 +40,10 @@ char *oid_to_hex(const struct object_id *oid);						/* same static buffer */
  * other invalid character.  end is only updated on success; otherwise, it is
  * unmodified.
  */
-int parse_oid_hex_algop(const char *hex, struct object_id *oid, const char **end,
-			const struct git_hash_algo *algo);
+#define parse_oid_hex_algop(hex, oid, end, algo) \
+	parse_oid_hex_algop_impl((hex), (oid), CONST_OUTPARAM((hex), (end)), (algo))
+int parse_oid_hex_algop_impl(const char *hex, struct object_id *oid, const char **end,
+			     const struct git_hash_algo *algo);
 
 /*
  * These functions work like get_oid_hex and parse_oid_hex, but they will parse
diff --git a/hook.c b/hook.c
index cc23276..d10eef4 100644
--- a/hook.c
+++ b/hook.c
@@ -5,12 +5,23 @@
 #include "environment.h"
 #include "gettext.h"
 #include "hook.h"
+#include "hook-list.h"
 #include "parse.h"
 #include "path.h"
 #include "run-command.h"
 #include "setup.h"
 #include "strbuf.h"
 #include "strmap.h"
+#include "thread-utils.h"
+
+bool is_known_hook(const char *name)
+{
+	const char **h;
+	for (h = hook_name_list; *h; h++)
+		if (!strcmp(*h, name))
+			return true;
+	return false;
+}
 
 const char *find_hook(struct repository *r, const char *name)
 {
@@ -116,18 +127,27 @@ struct hook_config_cache_entry {
 	char *command;
 	enum config_scope scope;
 	bool disabled;
+	bool parallel;
 };
 
 /*
  * Callback struct to collect all hook.* keys in a single config pass.
  * commands: friendly-name to command map.
  * event_hooks: event-name to list of friendly-names map.
- * disabled_hooks: set of friendly-names with hook.<friendly-name>.enabled = false.
+ * disabled_hooks: set of all names with hook.<name>.enabled = false; after
+ *                 parsing, names that are not friendly-names become event-level
+ *                 disables stored in r->disabled_events. This collects all.
+ * parallel_hooks: friendly-name to parallel flag.
+ * event_jobs: event-name to per-event jobs count (stored as uintptr_t, NULL == unset).
+ * jobs: value of the global hook.jobs key. Defaults to 0 if unset (stored in r->hook_jobs).
  */
 struct hook_all_config_cb {
 	struct strmap commands;
 	struct strmap event_hooks;
 	struct string_list disabled_hooks;
+	struct strmap parallel_hooks;
+	struct strmap event_jobs;
+	unsigned int jobs;
 };
 
 /* repo_config() callback that collects all hook.* configuration in one pass. */
@@ -143,6 +163,24 @@ static int hook_config_lookup_all(const char *key, const char *value,
 	if (parse_config_key(key, "hook", &name, &name_len, &subkey))
 		return 0;
 
+	/* Handle plain hook.<key> entries that have no hook name component. */
+	if (!name) {
+		if (!strcmp(subkey, "jobs") && value) {
+			int v;
+			if (!git_parse_int(value, &v))
+				warning(_("hook.jobs must be an integer, ignoring: '%s'"), value);
+			else if (v == -1)
+				data->jobs = online_cpus();
+			else if (v > 0)
+				data->jobs = v;
+			else
+				warning(_("hook.jobs must be a positive integer"
+					  " or -1, ignoring: '%s'"),
+					value);
+		}
+		return 0;
+	}
+
 	if (!value)
 		return config_error_nonbool(key);
 
@@ -158,8 +196,21 @@ static int hook_config_lookup_all(const char *key, const char *value,
 			strmap_for_each_entry(&data->event_hooks, &iter, e)
 				unsorted_string_list_remove(e->value, hook_name, 0);
 		} else {
-			struct string_list *hooks =
-				strmap_get(&data->event_hooks, value);
+			struct string_list *hooks;
+
+			if (is_known_hook(hook_name))
+				die(_("hook friendly-name '%s' collides with "
+				      "a known event name; please choose a "
+				      "different friendly-name"),
+				    hook_name);
+
+			if (!strcmp(hook_name, value))
+				warning(_("hook friendly-name '%s' is the "
+					  "same as its event; this may cause "
+					  "ambiguity with hook.%s.enabled"),
+					hook_name, hook_name);
+
+			hooks = strmap_get(&data->event_hooks, value);
 
 			if (!hooks) {
 				CALLOC_ARRAY(hooks, 1);
@@ -203,6 +254,31 @@ static int hook_config_lookup_all(const char *key, const char *value,
 		default:
 			break; /* ignore unrecognised values */
 		}
+	} else if (!strcmp(subkey, "parallel")) {
+		int v = git_parse_maybe_bool(value);
+		if (v >= 0)
+			strmap_put(&data->parallel_hooks, hook_name,
+				   (void *)(uintptr_t)v);
+		else
+			warning(_("hook.%s.parallel must be a boolean,"
+				  " ignoring: '%s'"),
+				hook_name, value);
+	} else if (!strcmp(subkey, "jobs")) {
+		int v;
+		if (!git_parse_int(value, &v))
+			warning(_("hook.%s.jobs must be an integer,"
+				  " ignoring: '%s'"),
+				hook_name, value);
+		else if (v == -1)
+			strmap_put(&data->event_jobs, hook_name,
+				   (void *)(uintptr_t)online_cpus());
+		else if (v > 0)
+			strmap_put(&data->event_jobs, hook_name,
+				   (void *)(uintptr_t)v);
+		else
+			warning(_("hook.%s.jobs must be a positive"
+				  " integer or -1, ignoring: '%s'"),
+				hook_name, value);
 	}
 
 	free(hook_name);
@@ -237,20 +313,78 @@ void hook_cache_clear(struct strmap *cache)
 	strmap_clear(cache, 0);
 }
 
+/*
+ * Return true if `name` is a hook friendly-name, i.e. it has at least one of
+ * .command, .event, or .parallel configured. These are the reliable clues
+ * that distinguish a friendly-name from an event name. Note: .enabled is
+ * deliberately excluded because it can appear under both namespaces.
+ */
+static int is_friendly_name(struct hook_all_config_cb *cb, const char *name)
+{
+	struct hashmap_iter iter;
+	struct strmap_entry *e;
+
+	if (strmap_get(&cb->commands, name) || strmap_get(&cb->parallel_hooks, name))
+		return 1;
+
+	strmap_for_each_entry(&cb->event_hooks, &iter, e) {
+		if (unsorted_string_list_lookup(e->value, name))
+			return 1;
+	}
+
+	return 0;
+}
+
+/* Warn if any name in event_jobs is also a hook friendly-name. */
+static void warn_jobs_on_friendly_names(struct hook_all_config_cb *cb_data)
+{
+	struct hashmap_iter iter;
+	struct strmap_entry *e;
+
+	strmap_for_each_entry(&cb_data->event_jobs, &iter, e) {
+		if (is_friendly_name(cb_data, e->key))
+			warning(_("hook.%s.jobs is set but '%s' looks like a "
+				  "hook friendly-name, not an event name; "
+				  "hook.<event>.jobs uses the event name "
+				  "(e.g. hook.post-receive.jobs), so this "
+				  "setting will be ignored"), e->key, e->key);
+	}
+}
+
 /* Populate `cache` with the complete hook configuration */
 static void build_hook_config_map(struct repository *r, struct strmap *cache)
 {
-	struct hook_all_config_cb cb_data;
+	struct hook_all_config_cb cb_data = { 0 };
 	struct hashmap_iter iter;
 	struct strmap_entry *e;
 
 	strmap_init(&cb_data.commands);
 	strmap_init(&cb_data.event_hooks);
 	string_list_init_dup(&cb_data.disabled_hooks);
+	strmap_init(&cb_data.parallel_hooks);
+	strmap_init(&cb_data.event_jobs);
 
-	/* Parse all configs in one run. */
+	/* Parse all configs in one run, capturing hook.* including hook.jobs. */
 	repo_config(r, hook_config_lookup_all, &cb_data);
 
+	warn_jobs_on_friendly_names(&cb_data);
+
+	/*
+	 * Populate disabled_events: names in disabled_hooks that are not
+	 * friendly-names are event-level switches (hook.<event>.enabled = false).
+	 * Names that are friendly-names are already handled per-hook via the
+	 * hook_config_cache_entry.disabled flag below.
+	 */
+	if (r) {
+		string_list_clear(&r->disabled_events, 0);
+		string_list_init_dup(&r->disabled_events);
+		for (size_t i = 0; i < cb_data.disabled_hooks.nr; i++) {
+			const char *n = cb_data.disabled_hooks.items[i].string;
+			if (!is_friendly_name(&cb_data, n))
+				string_list_append(&r->disabled_events, n);
+		}
+	}
+
 	/* Construct the cache from parsed configs. */
 	strmap_for_each_entry(&cb_data.event_hooks, &iter, e) {
 		struct string_list *hook_names = e->value;
@@ -266,6 +400,7 @@ static void build_hook_config_map(struct repository *r, struct strmap *cache)
 			struct hook_config_cache_entry *entry;
 			char *command;
 
+			bool is_par = !!strmap_get(&cb_data.parallel_hooks, hname);
 			bool is_disabled =
 				!!unsorted_string_list_lookup(
 					&cb_data.disabled_hooks, hname);
@@ -286,13 +421,20 @@ static void build_hook_config_map(struct repository *r, struct strmap *cache)
 			entry->command = xstrdup_or_null(command);
 			entry->scope = scope;
 			entry->disabled = is_disabled;
+			entry->parallel = is_par;
 			string_list_append(hooks, hname)->util = entry;
 		}
 
 		strmap_put(cache, e->key, hooks);
 	}
 
+	if (r) {
+		r->hook_jobs = cb_data.jobs;
+		r->event_jobs = cb_data.event_jobs;
+	}
+
 	strmap_clear(&cb_data.commands, 1);
+	strmap_clear(&cb_data.parallel_hooks, 0); /* values are uintptr_t, not heap ptrs */
 	string_list_clear(&cb_data.disabled_hooks, 0);
 	strmap_for_each_entry(&cb_data.event_hooks, &iter, e) {
 		string_list_clear(e->value, 0);
@@ -344,6 +486,8 @@ static void list_hooks_add_configured(struct repository *r,
 {
 	struct strmap *cache = get_hook_config_cache(r);
 	struct string_list *configured_hooks = strmap_get(cache, hookname);
+	bool event_is_disabled = r ? !!unsorted_string_list_lookup(&r->disabled_events,
+								   hookname) : 0;
 
 	/* Iterate through configured hooks and initialize internal states */
 	for (size_t i = 0; configured_hooks && i < configured_hooks->nr; i++) {
@@ -370,6 +514,8 @@ static void list_hooks_add_configured(struct repository *r,
 			entry->command ? xstrdup(entry->command) : NULL;
 		hook->u.configured.scope = entry->scope;
 		hook->u.configured.disabled = entry->disabled;
+		hook->u.configured.event_disabled = event_is_disabled;
+		hook->parallel = entry->parallel;
 
 		string_list_append(list, friendly_name)->util = hook;
 	}
@@ -381,6 +527,8 @@ static void list_hooks_add_configured(struct repository *r,
 	if (!r || !r->gitdir) {
 		hook_cache_clear(cache);
 		free(cache);
+		if (r)
+			string_list_clear(&r->disabled_events, 0);
 	}
 }
 
@@ -412,7 +560,7 @@ int hook_exists(struct repository *r, const char *name)
 	for (size_t i = 0; i < hooks->nr; i++) {
 		struct hook *h = hooks->items[i].util;
 		if (h->kind == HOOK_TRADITIONAL ||
-		    !h->u.configured.disabled) {
+		    (!h->u.configured.disabled && !h->u.configured.event_disabled)) {
 			exists = 1;
 			break;
 		}
@@ -435,7 +583,8 @@ static int pick_next_hook(struct child_process *cp,
 		if (hook_cb->hook_to_run_index >= hook_list->nr)
 			return 0;
 		h = hook_list->items[hook_cb->hook_to_run_index++].util;
-	} while (h->kind == HOOK_CONFIGURED && h->u.configured.disabled);
+	} while (h->kind == HOOK_CONFIGURED &&
+		 (h->u.configured.disabled || h->u.configured.event_disabled));
 
 	cp->no_stdin = 1;
 	strvec_pushv(&cp->env, hook_cb->options->env.v);
@@ -519,21 +668,136 @@ static void run_hooks_opt_clear(struct run_hooks_opt *options)
 	strvec_clear(&options->args);
 }
 
+/*
+ * When running in parallel, stdout must be merged into stderr so
+ * run-command can buffer and de-interleave outputs correctly. This
+ * applies even to hooks like pre-push that normally keep stdout and
+ * stderr separate: the user has opted into parallelism, so the output
+ * stream behavior changes accordingly.
+ */
+static void merge_output_if_parallel(struct run_hooks_opt *options)
+{
+	if (options->jobs > 1)
+		options->stdout_to_stderr = 1;
+}
+
+static void warn_non_parallel_hooks_override(unsigned int jobs,
+					     struct string_list *hook_list)
+{
+	/* Don't warn for hooks running sequentially. */
+	if (jobs == 1)
+		return;
+
+	for (size_t i = 0; i < hook_list->nr; i++) {
+		struct hook *h = hook_list->items[i].util;
+		if (h->kind == HOOK_CONFIGURED && !h->parallel)
+			warning(_("hook '%s' is not marked as parallel=true, "
+				  "running in parallel anyway due to -j%u"),
+				h->u.configured.friendly_name, jobs);
+	}
+}
+
+/* Resolve a hook.jobs config key, handling -1 as online_cpus(). */
+static void resolve_hook_config_jobs(struct repository *r,
+				     const char *key,
+				     unsigned int *jobs)
+{
+	int v;
+
+	if (repo_config_get_int(r, key, &v))
+		return;
+
+	if (v == -1)
+		*jobs = online_cpus();
+	else if (v > 0)
+		*jobs = v;
+	else
+		warning(_("%s must be a positive integer or -1,"
+			  " ignoring: %d"), key, v);
+}
+
+/* Determine how many jobs to use for hook execution. */
+static unsigned int get_hook_jobs(struct repository *r,
+				  struct run_hooks_opt *options,
+				  const char *hook_name,
+				  struct string_list *hook_list)
+{
+	/*
+	 * An explicit job count overrides everything else: this covers both
+	 * FORCE_SERIAL callers (for hooks that must never run in parallel)
+	 * and the -j flag from the CLI. The CLI override is intentional: users
+	 * may want to serialize hooks declared parallel or to parallelize more
+	 * aggressively than the default.
+	 */
+	if (options->jobs)
+		goto cleanup;
+
+	/*
+	 * Use hook.jobs from the already-parsed config cache (in-repo), or
+	 * fallback to a direct config lookup (out-of-repo).
+	 * Default to 1 (serial execution) on failure.
+	 */
+	options->jobs = 1;
+	if (r) {
+		if (r->gitdir && r->hook_config_cache) {
+			void *event_jobs;
+
+			if (r->hook_jobs)
+				options->jobs = r->hook_jobs;
+
+			event_jobs = strmap_get(&r->event_jobs, hook_name);
+			if (event_jobs)
+				options->jobs = (unsigned int)(uintptr_t)event_jobs;
+		} else {
+			char *key;
+
+			resolve_hook_config_jobs(r, "hook.jobs", &options->jobs);
+
+			key = xstrfmt("hook.%s.jobs", hook_name);
+			resolve_hook_config_jobs(r, key, &options->jobs);
+			free(key);
+		}
+	}
+
+	/*
+	 * Cap to serial any configured hook not marked as parallel = true.
+	 * This enforces the parallel = false default, even for "traditional"
+	 * hooks from the hookdir which cannot be marked parallel = true.
+	 * The same restriction applies whether jobs came from hook.jobs or
+	 * hook.<event>.jobs.
+	 */
+	for (size_t i = 0; i < hook_list->nr; i++) {
+		struct hook *h = hook_list->items[i].util;
+		if (h->kind == HOOK_CONFIGURED && !h->parallel) {
+			options->jobs = 1;
+			break;
+		}
+	}
+
+cleanup:
+	merge_output_if_parallel(options);
+	warn_non_parallel_hooks_override(options->jobs, hook_list);
+	return options->jobs;
+}
+
 int run_hooks_opt(struct repository *r, const char *hook_name,
 		  struct run_hooks_opt *options)
 {
+	struct string_list *hook_list = list_hooks(r, hook_name, options);
 	struct hook_cb_data cb_data = {
 		.rc = 0,
 		.hook_name = hook_name,
+		.hook_command_list = hook_list,
 		.options = options,
 	};
 	int ret = 0;
+	unsigned int jobs = get_hook_jobs(r, options, hook_name, hook_list);
 	const struct run_process_parallel_opts opts = {
 		.tr2_category = "hook",
 		.tr2_label = hook_name,
 
-		.processes = options->jobs,
-		.ungroup = options->jobs == 1,
+		.processes = jobs,
+		.ungroup = jobs == 1,
 
 		.get_next_task = pick_next_hook,
 		.start_failure = notify_start_failure,
@@ -549,9 +813,6 @@ int run_hooks_opt(struct repository *r, const char *hook_name,
 	if (options->path_to_stdin && options->feed_pipe)
 		BUG("options path_to_stdin and feed_pipe are mutually exclusive");
 
-	if (!options->jobs)
-		BUG("run_hooks_opt must be called with options.jobs >= 1");
-
 	/*
 	 * Ensure cb_data copy and free functions are either provided together,
 	 * or neither one is provided.
@@ -562,7 +823,6 @@ int run_hooks_opt(struct repository *r, const char *hook_name,
 	if (options->invoked_hook)
 		*options->invoked_hook = 0;
 
-	cb_data.hook_command_list = list_hooks(r, hook_name, options);
 	if (!cb_data.hook_command_list->nr) {
 		if (options->error_if_missing)
 			ret = error("cannot find a hook named %s", hook_name);
diff --git a/hook.h b/hook.h
index 5c5628d..b4372b6 100644
--- a/hook.h
+++ b/hook.h
@@ -32,10 +32,18 @@ struct hook {
 			const char *command;
 			enum config_scope scope;
 			bool disabled;
+			bool event_disabled;
 		} configured;
 	} u;
 
 	/**
+	 * Whether this hook may run in parallel with other hooks for the same
+	 * event. Only useful for configured (named) hooks. Traditional hooks
+	 * always default to 0 (serial). Set via `hook.<name>.parallel = true`.
+	 */
+	bool parallel;
+
+	/**
 	 * Opaque data pointer used to keep internal state across callback calls.
 	 *
 	 * It can be accessed directly via the third hook callback arg:
@@ -72,6 +80,8 @@ struct run_hooks_opt {
 	 *
 	 * If > 1, output will be buffered and de-interleaved (ungroup=0).
 	 * If == 1, output will be real-time (ungroup=1).
+	 * If == 0, the 'hook.jobs' config is used or, if the config is unset,
+	 * defaults to 1 (serial execution).
 	 */
 	unsigned int jobs;
 
@@ -97,8 +107,10 @@ struct run_hooks_opt {
 	 * Send the hook's stdout to stderr.
 	 *
 	 * This is the default behavior for all hooks except pre-push,
-	 * which has separate stdout and stderr streams for backwards
-	 * compatibility reasons.
+	 * which keeps stdout and stderr separate for backwards compatibility.
+	 * When parallel execution is requested (jobs > 1), get_hook_jobs()
+	 * overrides this to 1 for all hooks so run-command can de-interleave
+	 * their outputs correctly.
 	 */
 	unsigned int stdout_to_stderr:1;
 
@@ -152,10 +164,26 @@ struct run_hooks_opt {
 	hook_data_free_fn feed_pipe_cb_data_free;
 };
 
+/**
+ * Default initializer for hooks. Parallelism is opt-in: .jobs = 0 defers to
+ * the 'hook.jobs' config, falling back to serial (1) if unset.
+ */
 #define RUN_HOOKS_OPT_INIT { \
 	.env = STRVEC_INIT, \
 	.args = STRVEC_INIT, \
 	.stdout_to_stderr = 1, \
+	.jobs = 0, \
+}
+
+/**
+ * Initializer for hooks that must always run sequentially regardless of
+ * 'hook.jobs'. Use this when git knows the hook cannot safely be parallelized
+ * .jobs = 1 is non-overridable.
+ */
+#define RUN_HOOKS_OPT_INIT_FORCE_SERIAL { \
+	.env = STRVEC_INIT, \
+	.args = STRVEC_INIT, \
+	.stdout_to_stderr = 1, \
 	.jobs = 1, \
 }
 
@@ -208,6 +236,12 @@ void hook_free(void *p, const char *str);
 void hook_cache_clear(struct strmap *cache);
 
 /**
+ * Returns true if `name` is a recognized hook event name
+ * (e.g. "pre-commit", "post-receive").
+ */
+bool is_known_hook(const char *name);
+
+/**
  * Returns the path to the hook file, or NULL if the hook is missing
  * or disabled. Note that this points to static storage that will be
  * overwritten by further calls to find_hook and run_hook_*.
diff --git a/http-backend.c b/http-backend.c
index 1a171c5..c7566b1 100644
--- a/http-backend.c
+++ b/http-backend.c
@@ -809,7 +809,7 @@ int cmd_main(int argc UNUSED, const char **argv UNUSED)
 		not_found(&hdr, "Request not supported: '%s'", dir);
 
 	setup_path();
-	if (!enter_repo(dir, 0))
+	if (!enter_repo(the_repository, dir, 0))
 		not_found(&hdr, "Not a git repository: '%s'", dir);
 	if (!getenv("GIT_HTTP_EXPORT_ALL") &&
 	    access("git-daemon-export-ok", F_OK) )
diff --git a/http-fetch.c b/http-fetch.c
index 1922e23..f9b6ecb 100644
--- a/http-fetch.c
+++ b/http-fetch.c
@@ -109,7 +109,7 @@ int cmd_main(int argc, const char **argv)
 	struct strvec index_pack_args = STRVEC_INIT;
 	int ret;
 
-	setup_git_directory_gently(&nongit);
+	setup_git_directory_gently(the_repository, &nongit);
 
 	while (arg < argc && argv[arg][0] == '-') {
 		const char *p;
diff --git a/http-push.c b/http-push.c
index 9ae6062..520d6c3 100644
--- a/http-push.c
+++ b/http-push.c
@@ -99,7 +99,7 @@ static struct object_list *objects;
 
 struct repo {
 	char *url;
-	char *path;
+	const char *path;
 	int path_len;
 	int has_info_refs;
 	int can_update_info_refs;
@@ -1449,7 +1449,7 @@ static void one_remote_ref(const char *refname)
 	 */
 	if (repo->can_update_info_refs &&
 	    !odb_has_object(the_repository->objects, &ref->old_oid,
-			    HAS_OBJECT_RECHECK_PACKED | HAS_OBJECT_FETCH_PROMISOR)) {
+			    ODB_HAS_OBJECT_RECHECK_PACKED | ODB_HAS_OBJECT_FETCH_PROMISOR)) {
 		obj = lookup_unknown_object(the_repository, &ref->old_oid);
 		fprintf(stderr,	"  fetch %s for %s\n",
 			oid_to_hex(&ref->old_oid), refname);
@@ -1655,7 +1655,7 @@ static int delete_remote_branch(const char *pattern, int force)
 		if (is_null_oid(&head_oid))
 			return error("Unable to resolve remote HEAD");
 		if (!odb_has_object(the_repository->objects, &head_oid,
-				    HAS_OBJECT_RECHECK_PACKED | HAS_OBJECT_FETCH_PROMISOR))
+				    ODB_HAS_OBJECT_RECHECK_PACKED | ODB_HAS_OBJECT_FETCH_PROMISOR))
 			return error("Remote HEAD resolves to object %s\nwhich does not exist locally, perhaps you need to fetch?", oid_to_hex(&head_oid));
 
 		/* Remote branch must resolve to a known object */
@@ -1663,7 +1663,7 @@ static int delete_remote_branch(const char *pattern, int force)
 			return error("Unable to resolve remote branch %s",
 				     remote_ref->name);
 		if (!odb_has_object(the_repository->objects, &remote_ref->old_oid,
-				    HAS_OBJECT_RECHECK_PACKED | HAS_OBJECT_FETCH_PROMISOR))
+				    ODB_HAS_OBJECT_RECHECK_PACKED | ODB_HAS_OBJECT_FETCH_PROMISOR))
 			return error("Remote branch %s resolves to object %s\nwhich does not exist locally, perhaps you need to fetch?", remote_ref->name, oid_to_hex(&remote_ref->old_oid));
 
 		/* Remote branch must be an ancestor of remote HEAD */
@@ -1788,7 +1788,7 @@ int cmd_main(int argc, const char **argv)
 	if (delete_branch && rs.nr != 1)
 		die("You must specify only one branch name when deleting a remote branch");
 
-	gitdir = setup_git_directory();
+	gitdir = setup_git_directory(the_repository);
 
 	memset(remote_dir_exists, -1, 256);
 
@@ -1886,7 +1886,7 @@ int cmd_main(int argc, const char **argv)
 		    !is_null_oid(&ref->old_oid) &&
 		    !ref->force) {
 			if (!odb_has_object(the_repository->objects, &ref->old_oid,
-					    HAS_OBJECT_RECHECK_PACKED | HAS_OBJECT_FETCH_PROMISOR) ||
+					    ODB_HAS_OBJECT_RECHECK_PACKED | ODB_HAS_OBJECT_FETCH_PROMISOR) ||
 			    !ref_newer(&ref->peer_ref->new_oid,
 				       &ref->old_oid)) {
 				/*
diff --git a/http-walker.c b/http-walker.c
index e886e64..f252de0 100644
--- a/http-walker.c
+++ b/http-walker.c
@@ -139,7 +139,7 @@ static int fill_active_slot(void *data UNUSED)
 		obj_req = list_entry(pos, struct object_request, node);
 		if (obj_req->state == WAITING) {
 			if (odb_has_object(the_repository->objects, &obj_req->oid,
-					   HAS_OBJECT_RECHECK_PACKED | HAS_OBJECT_FETCH_PROMISOR))
+					   ODB_HAS_OBJECT_RECHECK_PACKED | ODB_HAS_OBJECT_FETCH_PROMISOR))
 				obj_req->state = COMPLETE;
 			else {
 				start_object_request(obj_req);
@@ -268,7 +268,7 @@ static void process_alternates_response(void *callback_data)
 				 */
 				const char *colon_ss = strstr(base,"://");
 				if (colon_ss) {
-					serverlen = (strchr(colon_ss + 3, '/')
+					serverlen = (strchrnul(colon_ss + 3, '/')
 						     - base);
 					okay = 1;
 				}
@@ -495,7 +495,7 @@ static int fetch_object(struct walker *walker, const struct object_id *oid)
 		return error("Couldn't find request for %s in the queue", hex);
 
 	if (odb_has_object(the_repository->objects, &obj_req->oid,
-			   HAS_OBJECT_RECHECK_PACKED | HAS_OBJECT_FETCH_PROMISOR)) {
+			   ODB_HAS_OBJECT_RECHECK_PACKED | ODB_HAS_OBJECT_FETCH_PROMISOR)) {
 		if (obj_req->req)
 			abort_http_object_request(&obj_req->req);
 		abort_object_request(obj_req);
diff --git a/http.c b/http.c
index d8d0168..ea9b168 100644
--- a/http.c
+++ b/http.c
@@ -138,6 +138,7 @@ static unsigned long empty_auth_useless =
 	CURLAUTH_BASIC
 	| CURLAUTH_DIGEST_IE
 	| CURLAUTH_DIGEST;
+static int empty_auth_try_negotiate;
 
 static struct curl_slist *pragma_header;
 static struct string_list extra_http_headers = STRING_LIST_INIT_DUP;
@@ -665,6 +666,22 @@ static void init_curl_http_auth(CURL *result)
 	}
 }
 
+void http_reauth_prepare(int all_capabilities)
+{
+	/*
+	 * If we deferred stripping Negotiate to give empty auth a
+	 * chance (auto mode), skip credential_fill on this retry so
+	 * that init_curl_http_auth() sends empty credentials and
+	 * libcurl can attempt Negotiate with the system ticket cache.
+	 */
+	if (empty_auth_try_negotiate &&
+	    !http_auth.password && !http_auth.credential &&
+	    (http_auth_methods & CURLAUTH_GSSNEGOTIATE))
+		return;
+
+	credential_fill(the_repository, &http_auth, all_capabilities);
+}
+
 /* *var must be free-able */
 static void var_override(char **var, char *value)
 {
@@ -744,11 +761,74 @@ static int has_proxy_cert_password(void)
 	return 1;
 }
 
+static const struct socks_proxy_type {
+	const char *name;
+	long curlsym;
+} socks_proxy_types[] = {
+	{ "socks", CURLPROXY_SOCKS4 },
+	{ "socks4", CURLPROXY_SOCKS4 },
+	{ "socks4a", CURLPROXY_SOCKS4A },
+	{ "socks5", CURLPROXY_SOCKS5 },
+	{ "socks5h", CURLPROXY_SOCKS5_HOSTNAME },
+};
+
+static const struct socks_proxy_type *find_socks_proxy_type(const char *protocol)
+{
+	int i;
+
+	if (!protocol)
+		return NULL;
+
+	for (i = 0; i < ARRAY_SIZE(socks_proxy_types); i++) {
+		if (!strcmp(socks_proxy_types[i].name, protocol))
+			return &socks_proxy_types[i];
+	}
+
+	return NULL;
+}
+
+static int is_socks_proxy_protocol(const char *protocol)
+{
+	return !!find_socks_proxy_type(protocol);
+}
+
+static int set_curl_proxy_type(CURL *result, const char *protocol)
+{
+	const struct socks_proxy_type *socks_proxy_type;
+
+	if (!protocol || !strcmp(protocol, "http"))
+		return 0;
+
+	socks_proxy_type = find_socks_proxy_type(protocol);
+	if (socks_proxy_type) {
+		curl_easy_setopt(result, CURLOPT_PROXYTYPE, socks_proxy_type->curlsym);
+		return 0;
+	}
+
+	if (!strcmp(protocol, "https")) {
+		curl_easy_setopt(result, CURLOPT_PROXYTYPE, (long)CURLPROXY_HTTPS);
+
+		if (http_proxy_ssl_cert)
+			curl_easy_setopt(result, CURLOPT_PROXY_SSLCERT,
+					 http_proxy_ssl_cert);
+
+		if (http_proxy_ssl_key)
+			curl_easy_setopt(result, CURLOPT_PROXY_SSLKEY,
+					 http_proxy_ssl_key);
+
+		if (has_proxy_cert_password())
+			curl_easy_setopt(result, CURLOPT_PROXY_KEYPASSWD,
+					 proxy_cert_auth.password);
+	}
+
+	return -1;
+}
+
 /* Return 1 if redactions have been made, 0 otherwise. */
 static int redact_sensitive_header(struct strbuf *header, size_t offset)
 {
 	int ret = 0;
-	const char *sensitive_header;
+	char *sensitive_header;
 
 	if (trace_curl_redact &&
 	    (skip_iprefix(header->buf + offset, "Authorization:", &sensitive_header) ||
@@ -765,7 +845,7 @@ static int redact_sensitive_header(struct strbuf *header, size_t offset)
 	} else if (trace_curl_redact &&
 		   skip_iprefix(header->buf + offset, "Cookie:", &sensitive_header)) {
 		struct strbuf redacted_header = STRBUF_INIT;
-		const char *cookie;
+		char *cookie;
 
 		while (isspace(*sensitive_header))
 			sensitive_header++;
@@ -1214,30 +1294,6 @@ static CURL *get_curl_handle(void)
 	} else if (curl_http_proxy) {
 		struct strbuf proxy = STRBUF_INIT;
 
-		if (starts_with(curl_http_proxy, "socks5h"))
-			curl_easy_setopt(result,
-				CURLOPT_PROXYTYPE, (long)CURLPROXY_SOCKS5_HOSTNAME);
-		else if (starts_with(curl_http_proxy, "socks5"))
-			curl_easy_setopt(result,
-				CURLOPT_PROXYTYPE, (long)CURLPROXY_SOCKS5);
-		else if (starts_with(curl_http_proxy, "socks4a"))
-			curl_easy_setopt(result,
-				CURLOPT_PROXYTYPE, (long)CURLPROXY_SOCKS4A);
-		else if (starts_with(curl_http_proxy, "socks"))
-			curl_easy_setopt(result,
-				CURLOPT_PROXYTYPE, (long)CURLPROXY_SOCKS4);
-		else if (starts_with(curl_http_proxy, "https")) {
-			curl_easy_setopt(result, CURLOPT_PROXYTYPE, (long)CURLPROXY_HTTPS);
-
-			if (http_proxy_ssl_cert)
-				curl_easy_setopt(result, CURLOPT_PROXY_SSLCERT, http_proxy_ssl_cert);
-
-			if (http_proxy_ssl_key)
-				curl_easy_setopt(result, CURLOPT_PROXY_SSLKEY, http_proxy_ssl_key);
-
-			if (has_proxy_cert_password())
-				curl_easy_setopt(result, CURLOPT_PROXY_KEYPASSWD, proxy_cert_auth.password);
-		}
 		if (strstr(curl_http_proxy, "://"))
 			credential_from_url(&proxy_auth, curl_http_proxy);
 		else {
@@ -1247,6 +1303,10 @@ static CURL *get_curl_handle(void)
 			strbuf_release(&url);
 		}
 
+		if (set_curl_proxy_type(result, proxy_auth.protocol) < 0)
+			die("Invalid proxy URL '%s': unsupported proxy scheme '%s'",
+			    curl_http_proxy, proxy_auth.protocol);
+
 		if (!proxy_auth.host)
 			die("Invalid proxy URL '%s'", curl_http_proxy);
 
@@ -1257,7 +1317,7 @@ static CURL *get_curl_handle(void)
 			if (ver->version_num < 0x075400)
 				die("libcurl 7.84 or later is required to support paths in proxy URLs");
 
-			if (!starts_with(proxy_auth.protocol, "socks"))
+			if (!is_socks_proxy_protocol(proxy_auth.protocol))
 				die("Invalid proxy URL '%s': only SOCKS proxies support paths",
 				    curl_http_proxy);
 
@@ -1890,7 +1950,18 @@ static int handle_curl_result(struct slot_results *results)
 				http_proactive_auth = PROACTIVE_AUTH_NONE;
 			return HTTP_NOAUTH;
 		} else {
-			http_auth_methods &= ~CURLAUTH_GSSNEGOTIATE;
+			if (curl_empty_auth == -1 &&
+			    !empty_auth_try_negotiate &&
+			    (results->auth_avail & CURLAUTH_GSSNEGOTIATE)) {
+				/*
+				 * In auto mode, give Negotiate a chance via
+				 * empty auth before stripping it. If it fails,
+				 * we will strip it on the next 401.
+				 */
+				empty_auth_try_negotiate = 1;
+			} else {
+				http_auth_methods &= ~CURLAUTH_GSSNEGOTIATE;
+			}
 			if (results->auth_avail) {
 				http_auth_methods &= results->auth_avail;
 				http_auth_methods_restricted = 1;
@@ -2398,7 +2469,7 @@ static int http_request_recoverable(const char *url,
 				sleep(retry_delay);
 			}
 		} else if (ret == HTTP_REAUTH) {
-			credential_fill(the_repository, &http_auth, 1);
+			http_reauth_prepare(1);
 		}
 
 		ret = http_request(url, result, target, options);
diff --git a/http.h b/http.h
index f9ee888..729c519 100644
--- a/http.h
+++ b/http.h
@@ -76,6 +76,12 @@ extern int http_is_verbose;
 extern ssize_t http_post_buffer;
 extern struct credential http_auth;
 
+/**
+ * Prepare for an HTTP re-authentication retry. This fills credentials
+ * via credential_fill() so the next request can include them.
+ */
+void http_reauth_prepare(int all_capabilities);
+
 extern char curl_errorstr[CURL_ERROR_SIZE];
 
 enum http_follow_config {
diff --git a/imap-send.c b/imap-send.c
index af02c6a..cfd6a51 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -1799,7 +1799,7 @@ int cmd_main(int argc, const char **argv)
 	int nongit_ok;
 	int ret;
 
-	setup_git_directory_gently(&nongit_ok);
+	setup_git_directory_gently(the_repository, &nongit_ok);
 	repo_config(the_repository, git_imap_config, &server);
 
 	argc = parse_options(argc, (const char **)argv, "", imap_send_options, imap_send_usage, 0);
diff --git a/line-log.c b/line-log.c
index eeaf684..346c60c 100644
--- a/line-log.c
+++ b/line-log.c
@@ -589,7 +589,7 @@ parse_lines(struct repository *r, struct commit *commit,
 		range_part = xstrndup(item->string, name_part - item->string);
 		name_part++;
 
-		full_name = prefix_path(prefix, prefix ? strlen(prefix) : 0,
+		full_name = prefix_path(r, prefix, prefix ? strlen(prefix) : 0,
 					name_part);
 
 		spec = alloc_filespec(full_name);
@@ -858,173 +858,33 @@ static void queue_diffs(struct line_log_data *range,
 	diff_queue_clear(&diff_queued_diff);
 	diff_tree_oid(parent_tree_oid, tree_oid, "", opt);
 	if (opt->detect_rename && diff_might_be_rename()) {
-		/* must look at the full tree diff to detect renames */
-		clear_pathspec(&opt->pathspec);
-		diff_queue_clear(&diff_queued_diff);
+		struct diff_options rename_opts;
 
-		diff_tree_oid(parent_tree_oid, tree_oid, "", opt);
+		/*
+		 * Build a private diff_options for rename detection so
+		 * that any user-specified options on the original opts
+		 * (e.g. pickaxe) cannot discard diff pairs needed for
+		 * rename tracking.  Similar to blame's find_rename().
+		 */
+		repo_diff_setup(opt->repo, &rename_opts);
+		rename_opts.flags.recursive = 1;
+		rename_opts.detect_rename = opt->detect_rename;
+		rename_opts.rename_score = opt->rename_score;
+		rename_opts.output_format = DIFF_FORMAT_NO_OUTPUT;
+		diff_setup_done(&rename_opts);
+
+		/* must look at the full tree diff to detect renames */
+		diff_queue_clear(&diff_queued_diff);
+		diff_tree_oid(parent_tree_oid, tree_oid, "", &rename_opts);
 
 		filter_diffs_for_paths(range, 1);
-		diffcore_std(opt);
+		diffcore_std(&rename_opts);
 		filter_diffs_for_paths(range, 0);
+		diff_free(&rename_opts);
 	}
 	move_diff_queue(queue, &diff_queued_diff);
 }
 
-static char *get_nth_line(long line, unsigned long *ends, void *data)
-{
-	if (line == 0)
-		return (char *)data;
-	else
-		return (char *)data + ends[line] + 1;
-}
-
-static void print_line(const char *prefix, char first,
-		       long line, unsigned long *ends, void *data,
-		       const char *color, const char *reset, FILE *file)
-{
-	char *begin = get_nth_line(line, ends, data);
-	char *end = get_nth_line(line+1, ends, data);
-	int had_nl = 0;
-
-	if (end > begin && end[-1] == '\n') {
-		end--;
-		had_nl = 1;
-	}
-
-	fputs(prefix, file);
-	fputs(color, file);
-	putc(first, file);
-	fwrite(begin, 1, end-begin, file);
-	fputs(reset, file);
-	putc('\n', file);
-	if (!had_nl)
-		fputs("\\ No newline at end of file\n", file);
-}
-
-static void dump_diff_hacky_one(struct rev_info *rev, struct line_log_data *range)
-{
-	unsigned int i, j = 0;
-	long p_lines, t_lines;
-	unsigned long *p_ends = NULL, *t_ends = NULL;
-	struct diff_filepair *pair = range->pair;
-	struct diff_ranges *diff = &range->diff;
-
-	struct diff_options *opt = &rev->diffopt;
-	const char *prefix = diff_line_prefix(opt);
-	const char *c_reset = diff_get_color(opt->use_color, DIFF_RESET);
-	const char *c_frag = diff_get_color(opt->use_color, DIFF_FRAGINFO);
-	const char *c_meta = diff_get_color(opt->use_color, DIFF_METAINFO);
-	const char *c_old = diff_get_color(opt->use_color, DIFF_FILE_OLD);
-	const char *c_new = diff_get_color(opt->use_color, DIFF_FILE_NEW);
-	const char *c_context = diff_get_color(opt->use_color, DIFF_CONTEXT);
-
-	if (!pair || !diff)
-		goto out;
-
-	if (pair->one->oid_valid)
-		fill_line_ends(rev->diffopt.repo, pair->one, &p_lines, &p_ends);
-	fill_line_ends(rev->diffopt.repo, pair->two, &t_lines, &t_ends);
-
-	fprintf(opt->file, "%s%sdiff --git a/%s b/%s%s\n", prefix, c_meta, pair->one->path, pair->two->path, c_reset);
-	fprintf(opt->file, "%s%s--- %s%s%s\n", prefix, c_meta,
-	       pair->one->oid_valid ? "a/" : "",
-	       pair->one->oid_valid ? pair->one->path : "/dev/null",
-	       c_reset);
-	fprintf(opt->file, "%s%s+++ b/%s%s\n", prefix, c_meta, pair->two->path, c_reset);
-	for (i = 0; i < range->ranges.nr; i++) {
-		long p_start, p_end;
-		long t_start = range->ranges.ranges[i].start;
-		long t_end = range->ranges.ranges[i].end;
-		long t_cur = t_start;
-		unsigned int j_last;
-
-		/*
-		 * If a diff range touches multiple line ranges, then all
-		 * those line ranges should be shown, so take a step back if
-		 * the current line range is still in the previous diff range
-		 * (even if only partially).
-		 */
-		if (j > 0 && diff->target.ranges[j-1].end > t_start)
-			j--;
-
-		while (j < diff->target.nr && diff->target.ranges[j].end < t_start)
-			j++;
-		if (j == diff->target.nr || diff->target.ranges[j].start >= t_end)
-			continue;
-
-		/* Scan ahead to determine the last diff that falls in this range */
-		j_last = j;
-		while (j_last < diff->target.nr && diff->target.ranges[j_last].start < t_end)
-			j_last++;
-		if (j_last > j)
-			j_last--;
-
-		/*
-		 * Compute parent hunk headers: we know that the diff
-		 * has the correct line numbers (but not all hunks).
-		 * So it suffices to shift the start/end according to
-		 * the line numbers of the first/last hunk(s) that
-		 * fall in this range.
-		 */
-		if (t_start < diff->target.ranges[j].start)
-			p_start = diff->parent.ranges[j].start - (diff->target.ranges[j].start-t_start);
-		else
-			p_start = diff->parent.ranges[j].start;
-		if (t_end > diff->target.ranges[j_last].end)
-			p_end = diff->parent.ranges[j_last].end + (t_end-diff->target.ranges[j_last].end);
-		else
-			p_end = diff->parent.ranges[j_last].end;
-
-		if (!p_start && !p_end) {
-			p_start = -1;
-			p_end = -1;
-		}
-
-		/* Now output a diff hunk for this range */
-		fprintf(opt->file, "%s%s@@ -%ld,%ld +%ld,%ld @@%s\n",
-		       prefix, c_frag,
-		       p_start+1, p_end-p_start, t_start+1, t_end-t_start,
-		       c_reset);
-		while (j < diff->target.nr && diff->target.ranges[j].start < t_end) {
-			int k;
-			for (; t_cur < diff->target.ranges[j].start; t_cur++)
-				print_line(prefix, ' ', t_cur, t_ends, pair->two->data,
-					   c_context, c_reset, opt->file);
-			for (k = diff->parent.ranges[j].start; k < diff->parent.ranges[j].end; k++)
-				print_line(prefix, '-', k, p_ends, pair->one->data,
-					   c_old, c_reset, opt->file);
-			for (; t_cur < diff->target.ranges[j].end && t_cur < t_end; t_cur++)
-				print_line(prefix, '+', t_cur, t_ends, pair->two->data,
-					   c_new, c_reset, opt->file);
-			j++;
-		}
-		for (; t_cur < t_end; t_cur++)
-			print_line(prefix, ' ', t_cur, t_ends, pair->two->data,
-				   c_context, c_reset, opt->file);
-	}
-
-out:
-	free(p_ends);
-	free(t_ends);
-}
-
-/*
- * NEEDSWORK: manually building a diff here is not the Right
- * Thing(tm).  log -L should be built into the diff pipeline.
- */
-static void dump_diff_hacky(struct rev_info *rev, struct line_log_data *range)
-{
-	const char *prefix = diff_line_prefix(&rev->diffopt);
-
-	fprintf(rev->diffopt.file, "%s\n", prefix);
-
-	while (range) {
-		dump_diff_hacky_one(rev, range);
-		range = range->next;
-	}
-}
-
 /*
  * Unlike most other functions, this destructively operates on
  * 'range'.
@@ -1088,7 +948,7 @@ static int process_diff_filepair(struct rev_info *rev,
 
 static struct diff_filepair *diff_filepair_dup(struct diff_filepair *pair)
 {
-	struct diff_filepair *new_filepair = xmalloc(sizeof(struct diff_filepair));
+	struct diff_filepair *new_filepair = xcalloc(1, sizeof(struct diff_filepair));
 	new_filepair->one = pair->one;
 	new_filepair->two = pair->two;
 	new_filepair->one->count++;
@@ -1146,11 +1006,25 @@ static int process_all_files(struct line_log_data **range_out,
 
 int line_log_print(struct rev_info *rev, struct commit *commit)
 {
-
 	show_log(rev);
 	if (!(rev->diffopt.output_format & DIFF_FORMAT_NO_OUTPUT)) {
 		struct line_log_data *range = lookup_line_range(rev, commit);
-		dump_diff_hacky(rev, range);
+		struct line_log_data *r;
+		const char *prefix = diff_line_prefix(&rev->diffopt);
+
+		fprintf(rev->diffopt.file, "%s\n", prefix);
+
+		for (r = range; r; r = r->next) {
+			if (r->pair) {
+				struct diff_filepair *p =
+					diff_filepair_dup(r->pair);
+				p->line_ranges = &r->ranges;
+				diff_q(&diff_queued_diff, p);
+			}
+		}
+
+		diffcore_std(&rev->diffopt);
+		diff_flush(&rev->diffopt);
 	}
 	return 1;
 }
diff --git a/line-log.h b/line-log.h
index e9dadbc..04a6ea6 100644
--- a/line-log.h
+++ b/line-log.h
@@ -1,22 +1,12 @@
 #ifndef LINE_LOG_H
 #define LINE_LOG_H
 
+#include "diffcore.h" /* struct range, struct range_set */
+
 struct rev_info;
 struct commit;
 struct string_list;
 
-/* A range [start,end].  Lines are numbered starting at 0, and the
- * ranges include start but exclude end. */
-struct range {
-	long start, end;
-};
-
-/* A set of ranges.  The ranges must always be disjoint and sorted. */
-struct range_set {
-	unsigned int alloc, nr;
-	struct range *ranges;
-};
-
 /* A diff, encoded as the set of pre- and post-image ranges where the
  * files differ. A pair of ranges corresponds to a hunk. */
 struct diff_ranges {
diff --git a/list-objects-filter-options.c b/list-objects-filter-options.c
index cef67e5..bc5d98f 100644
--- a/list-objects-filter-options.c
+++ b/list-objects-filter-options.c
@@ -378,7 +378,7 @@ void partial_clone_register(
 			 */
 			return;
 	} else {
-		if (upgrade_repository_format(1) < 0)
+		if (upgrade_repository_format(the_repository, 1) < 0)
 			die(_("unable to upgrade repository format to support partial clone"));
 
 		/* Add promisor config for the remote */
diff --git a/list-objects.c b/list-objects.c
index 91b23e2..724d723 100644
--- a/list-objects.c
+++ b/list-objects.c
@@ -75,7 +75,7 @@ static void process_blob(struct traversal_context *ctx,
 	 */
 	if (ctx->revs->exclude_promisor_objects &&
 	    !odb_has_object(the_repository->objects, &obj->oid,
-			    HAS_OBJECT_RECHECK_PACKED | HAS_OBJECT_FETCH_PROMISOR) &&
+			    ODB_HAS_OBJECT_RECHECK_PACKED | ODB_HAS_OBJECT_FETCH_PROMISOR) &&
 	    is_promisor_object(ctx->revs->repo, &obj->oid))
 		return;
 
diff --git a/loose.c b/loose.c
index 07333be..f7a3dd1 100644
--- a/loose.c
+++ b/loose.c
@@ -57,7 +57,7 @@ static int insert_loose_map(struct odb_source *source,
 	inserted |= insert_oid_pair(map->to_compat, oid, compat_oid);
 	inserted |= insert_oid_pair(map->to_storage, compat_oid, oid);
 	if (inserted)
-		oidtree_insert(files->loose->cache, compat_oid);
+		oidtree_insert(files->loose->cache, compat_oid, NULL);
 
 	return inserted;
 }
diff --git a/merge-ort.c b/merge-ort.c
index 00923ce..544be9e 100644
--- a/merge-ort.c
+++ b/merge-ort.c
@@ -2954,32 +2954,6 @@ static int process_renames(struct merge_options *opt,
 			continue;
 
 		/*
-		 * Rename caching from a previous commit might give us an
-		 * irrelevant rename for the current commit.
-		 *
-		 * Imagine:
-		 *     foo/A -> bar/A
-		 * was a cached rename for the upstream side from the
-		 * previous commit (without the directories being renamed),
-		 * but the next commit being replayed
-		 *     * does NOT add or delete files
-		 *     * does NOT have directory renames
-		 *     * does NOT modify any files under bar/
-		 *     * does NOT modify foo/A
-		 *     * DOES modify other files under foo/ (otherwise the
-		 *       !oldinfo check above would have already exited for
-		 *       us)
-		 * In such a case, our trivial directory resolution will
-		 * have already merged bar/, and our attempt to process
-		 * the cached
-		 *     foo/A -> bar/A
-		 * would be counterproductive, and lack the necessary
-		 * information anyway.  Skip such renames.
-		 */
-		if (!newinfo)
-			continue;
-
-		/*
 		 * diff_filepairs have copies of pathnames, thus we have to
 		 * use standard 'strcmp()' (negated) instead of '=='.
 		 */
@@ -3330,6 +3304,28 @@ static void use_cached_pairs(struct merge_options *opt,
 			new_name = old_name;
 
 		/*
+		 * If this is a rename and the target path is either
+		 * absent from opt->priv->paths (because a parent
+		 * directory was trivially resolved) or already cleanly
+		 * resolved (e.g. all three sides agree on its content),
+		 * the cached rename is irrelevant for this commit.
+		 * Skip it here rather than in process_renames() to
+		 * preserve VERIFY_CI(newinfo)'s ability to catch bugs
+		 * for non-cached renames (see 979ee83e8a90 (merge-ort:
+		 * fix corner case recursive submodule/directory conflict
+		 * handling, 2025-12-29) for an example of a bug that
+		 * assertion caught).  The rename remains in cached_pairs
+		 * for use in subsequent commits.
+		 */
+		if (entry->value) {
+			struct merged_info *mi;
+
+			mi = strmap_get(&opt->priv->paths, new_name);
+			if (!mi || mi->clean)
+				continue;
+		}
+
+		/*
 		 * cached_pairs has *copies* of old_name and new_name,
 		 * because it has to persist across merges.  Since
 		 * pool_alloc_filespec() will just re-use the existing
diff --git a/meson.build b/meson.build
index 8309942..064fe2e 100644
--- a/meson.build
+++ b/meson.build
@@ -404,7 +404,9 @@
   'odb.c',
   'odb/source.c',
   'odb/source-files.c',
+  'odb/source-inmemory.c',
   'odb/streaming.c',
+  'odb/transaction.c',
   'oid-array.c',
   'oidmap.c',
   'oidset.c',
@@ -563,6 +565,18 @@
   env: script_environment,
 )
 
+libgit_sources += custom_target(
+  input: 'Documentation/githooks.adoc',
+  output: 'hook-list.h',
+  command: [
+    shell,
+    meson.current_source_dir() + '/tools/generate-hooklist.sh',
+    meson.current_source_dir(),
+    '@OUTPUT@',
+  ],
+  env: script_environment,
+)
+
 builtin_sources = [
   'builtin/add.c',
   'builtin/am.c',
@@ -686,6 +700,7 @@
   'builtin/update-server-info.c',
   'builtin/upload-archive.c',
   'builtin/upload-pack.c',
+  'builtin/url-parse.c',
   'builtin/var.c',
   'builtin/verify-commit.c',
   'builtin/verify-pack.c',
@@ -698,7 +713,6 @@
   ':!contrib',
   ':!compat/inet_ntop.c',
   ':!compat/inet_pton.c',
-  ':!compat/nedmalloc',
   ':!compat/obstack.*',
   ':!compat/poll',
   ':!compat/regex',
@@ -739,18 +753,6 @@
   env: script_environment,
 )
 
-builtin_sources += custom_target(
-  input: 'Documentation/githooks.adoc',
-  output: 'hook-list.h',
-  command: [
-    shell,
-    meson.current_source_dir() + '/tools/generate-hooklist.sh',
-    meson.current_source_dir(),
-    '@OUTPUT@',
-  ],
-  env: script_environment,
-)
-
 # This contains the variables for GIT-BUILD-OPTIONS, which we use to propagate
 # build options to our tests.
 build_options_config = configuration_data()
@@ -866,6 +868,12 @@
       libgit_c_args += cflag
     endif
   endforeach
+
+  # Clang generates warnings when compiling glibc 2.43 because of the use of
+  # _Generic.
+  if compiler.get_id() == 'clang'
+    libgit_c_args += '-Wno-c11-extensions'
+  endif
 endif
 
 if get_option('breaking_changes')
@@ -1285,7 +1293,6 @@
     'compat/win32/pthread.c',
     'compat/win32/syslog.c',
     'compat/win32mmap.c',
-    'compat/nedmalloc/nedmalloc.c',
   ]
 
   libgit_c_args += [
@@ -1344,10 +1351,17 @@
 endif
 
 fsmonitor_backend = ''
+fsmonitor_os = ''
 if host_machine.system() == 'windows'
   fsmonitor_backend = 'win32'
+  fsmonitor_os = 'win32'
+elif host_machine.system() == 'linux' and threads.found() and compiler.has_header('linux/magic.h')
+  fsmonitor_backend = 'linux'
+  fsmonitor_os = 'unix'
+  libgit_c_args += '-DHAVE_LINUX_MAGIC_H'
 elif host_machine.system() == 'darwin'
   fsmonitor_backend = 'darwin'
+  fsmonitor_os = 'unix'
   libgit_dependencies += dependency('CoreServices')
 endif
 if fsmonitor_backend != ''
@@ -1356,14 +1370,14 @@
 
   compat_sources += [
     'compat/fsmonitor/fsm-health-' + fsmonitor_backend + '.c',
-    'compat/fsmonitor/fsm-ipc-' + fsmonitor_backend + '.c',
+    'compat/fsmonitor/fsm-ipc-' + fsmonitor_os + '.c',
     'compat/fsmonitor/fsm-listen-' + fsmonitor_backend + '.c',
     'compat/fsmonitor/fsm-path-utils-' + fsmonitor_backend + '.c',
-    'compat/fsmonitor/fsm-settings-' + fsmonitor_backend + '.c',
+    'compat/fsmonitor/fsm-settings-' + fsmonitor_os + '.c',
   ]
 endif
 build_options_config.set_quoted('FSMONITOR_DAEMON_BACKEND', fsmonitor_backend)
-build_options_config.set_quoted('FSMONITOR_OS_SETTINGS', fsmonitor_backend)
+build_options_config.set_quoted('FSMONITOR_OS_SETTINGS', fsmonitor_os)
 
 if not get_option('b_sanitize').contains('address') and get_option('regex').allowed() and compiler.has_header('regex.h') and compiler.get_define('REG_STARTEND', prefix: '#include <regex.h>') != ''
   build_options_config.set('NO_REGEX', '')
@@ -1429,7 +1443,6 @@
   'initgroups' : [],
   'strtoumax' : ['strtoumax.c', 'strtoimax.c'],
   'pread' : ['pread.c'],
-  'writev' : ['writev.c'],
 }
 
 if host_machine.system() == 'windows'
@@ -1745,8 +1758,7 @@
 )
 libgit_sources += version_def_h
 
-cargo = find_program('cargo', dirs: program_path, native: true, required: get_option('rust'))
-rust_option = get_option('rust').disable_auto_if(not cargo.found())
+rust_option = get_option('rust')
 if rust_option.allowed()
   subdir('src')
   libgit_c_args += '-DWITH_RUST'
diff --git a/meson_options.txt b/meson_options.txt
index 659cbb2..80a8025 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -77,7 +77,7 @@
 # Build tweaks.
 option('breaking_changes', type: 'boolean', value: false,
   description: 'Enable upcoming breaking changes.')
-option('rust', type: 'feature', value: 'auto',
+option('rust', type: 'feature', value: 'enabled',
   description: 'Enable building with Rust.')
 option('macos_use_homebrew_gettext', type: 'boolean', value: true,
   description: 'Use gettext from Homebrew instead of the slightly-broken system-provided one.')
diff --git a/midx-write.c b/midx-write.c
index 0ff2e45..561e9ee 100644
--- a/midx-write.c
+++ b/midx-write.c
@@ -29,8 +29,7 @@ extern void clear_midx_files_ext(struct odb_source *source, const char *ext,
 				 const char *keep_hash);
 extern void clear_incremental_midx_files_ext(struct odb_source *source,
 					     const char *ext,
-					     const char **keep_hashes,
-					     uint32_t hashes_nr);
+					     const struct strvec *keep_hashes);
 extern int cmp_idx_or_pack_name(const char *idx_or_pack_name,
 				const char *idx_name);
 
@@ -1109,8 +1108,7 @@ static int link_midx_to_chain(struct multi_pack_index *m)
 }
 
 static void clear_midx_files(struct odb_source *source,
-			     const char **hashes, uint32_t hashes_nr,
-			     unsigned incremental)
+			     const struct strvec *hashes, unsigned incremental)
 {
 	/*
 	 * if incremental:
@@ -1124,13 +1122,15 @@ static void clear_midx_files(struct odb_source *source,
 	 */
 	struct strbuf buf = STRBUF_INIT;
 	const char *exts[] = { MIDX_EXT_BITMAP, MIDX_EXT_REV, MIDX_EXT_MIDX };
-	uint32_t i, j;
+	uint32_t i;
 
 	for (i = 0; i < ARRAY_SIZE(exts); i++) {
-		clear_incremental_midx_files_ext(source, exts[i],
-						 hashes, hashes_nr);
-		for (j = 0; j < hashes_nr; j++)
-			clear_midx_files_ext(source, exts[i], hashes[j]);
+		clear_incremental_midx_files_ext(source, exts[i], hashes);
+		if (hashes) {
+			for (size_t j = 0; j < hashes->nr; j++)
+				clear_midx_files_ext(source, exts[i],
+						     hashes->v[j]);
+		}
 	}
 
 	if (incremental)
@@ -1152,7 +1152,7 @@ static bool midx_needs_update(struct multi_pack_index *midx, struct write_midx_c
 
 	/*
 	 * Ensure that we have a valid checksum before consulting the
-	 * exisiting MIDX in order to determine if we can avoid an
+	 * existing MIDX in order to determine if we can avoid an
 	 * update.
 	 *
 	 * This is necessary because the given MIDX is loaded directly
@@ -1208,14 +1208,16 @@ static bool midx_needs_update(struct multi_pack_index *midx, struct write_midx_c
 			BUG("same pack added twice?");
 	}
 
-	for (uint32_t i = 0; i < ctx->nr; i++) {
-		strbuf_reset(&buf);
-		strbuf_addstr(&buf, midx->pack_names[i]);
-		strbuf_strip_suffix(&buf, ".idx");
+	for (struct multi_pack_index *m = midx; m; m = m->base_midx) {
+		for (uint32_t i = 0; i < m->num_packs; i++) {
+			strbuf_reset(&buf);
+			strbuf_addstr(&buf, m->pack_names[i]);
+			strbuf_strip_suffix(&buf, ".idx");
 
-		if (!strset_contains(&packs, buf.buf))
-			goto out;
-		strset_remove(&packs, buf.buf);
+			if (!strset_contains(&packs, buf.buf))
+				goto out;
+			strset_remove(&packs, buf.buf);
+		}
 	}
 
 	needed = false;
@@ -1245,6 +1247,7 @@ struct write_midx_opts {
 
 	const char *preferred_pack_name;
 	const char *refs_snapshot;
+	const char *incremental_base;
 	unsigned flags;
 };
 
@@ -1255,19 +1258,17 @@ static int write_midx_internal(struct write_midx_opts *opts)
 	unsigned char midx_hash[GIT_MAX_RAWSZ];
 	uint32_t start_pack;
 	struct hashfile *f = NULL;
-	struct lock_file lk;
+	struct lock_file lk = LOCK_INIT;
 	struct tempfile *incr;
 	struct write_midx_context ctx = {
 		.preferred_pack_idx = NO_PREFERRED_PACK,
-		.version = MIDX_VERSION_V2,
 	 };
 	struct multi_pack_index *midx_to_free = NULL;
 	int bitmapped_packs_concat_len = 0;
 	int pack_name_concat_len = 0;
 	int dropped_packs = 0;
 	int result = -1;
-	const char **keep_hashes = NULL;
-	size_t keep_hashes_nr = 0;
+	struct strvec keep_hashes = STRVEC_INIT;
 	struct chunkfile *cf;
 
 	trace2_region_enter("midx", "write_midx_internal", r);
@@ -1275,6 +1276,9 @@ static int write_midx_internal(struct write_midx_opts *opts)
 	ctx.repo = r;
 	ctx.source = opts->source;
 
+	ctx.version = ((opts->flags & MIDX_WRITE_COMPACT)
+		       ? MIDX_VERSION_V2
+		       : MIDX_VERSION_V1);
 	repo_config_get_int(ctx.repo, "midx.version", &ctx.version);
 	if (ctx.version != MIDX_VERSION_V1 && ctx.version != MIDX_VERSION_V2)
 		die(_("unknown MIDX version: %d"), ctx.version);
@@ -1327,11 +1331,32 @@ static int write_midx_internal(struct write_midx_opts *opts)
 
 	/*
 	 * If compacting MIDX layer(s) in the range [from, to], then the
-	 * compacted MIDX will share the same base MIDX as 'from'.
+	 * compacted MIDX will share the same base MIDX as 'from',
+	 * unless a custom --base is specified (see below).
 	 */
 	if (ctx.compact)
 		ctx.base_midx = ctx.compact_from->base_midx;
 
+	if (opts->incremental_base) {
+		if (!strcmp(opts->incremental_base, "none")) {
+			ctx.base_midx = NULL;
+		} else {
+			while (ctx.base_midx) {
+				const char *cmp = midx_get_checksum_hex(ctx.base_midx);
+				if (!strcmp(opts->incremental_base, cmp))
+					break;
+
+				ctx.base_midx = ctx.base_midx->base_midx;
+			}
+
+			if (!ctx.base_midx) {
+				error(_("could not find base MIDX '%s'"),
+				      opts->incremental_base);
+				goto cleanup;
+			}
+		}
+	}
+
 	ctx.nr = 0;
 	ctx.alloc = ctx.m ? ctx.m->num_packs + ctx.m->num_packs_in_base : 16;
 	ctx.info = NULL;
@@ -1598,11 +1623,14 @@ static int write_midx_internal(struct write_midx_opts *opts)
 	}
 
 	if (ctx.incremental) {
-		struct strbuf lock_name = STRBUF_INIT;
+		if (!(opts->flags & MIDX_WRITE_NO_CHAIN)) {
+			struct strbuf lock_name = STRBUF_INIT;
 
-		get_midx_chain_filename(opts->source, &lock_name);
-		hold_lock_file_for_update(&lk, lock_name.buf, LOCK_DIE_ON_ERROR);
-		strbuf_release(&lock_name);
+			get_midx_chain_filename(opts->source, &lock_name);
+			hold_lock_file_for_update(&lk, lock_name.buf,
+						  LOCK_DIE_ON_ERROR);
+			strbuf_release(&lock_name);
+		}
 
 		incr = mks_tempfile_m(midx_name.buf, 0444);
 		if (!incr) {
@@ -1704,33 +1732,23 @@ static int write_midx_internal(struct write_midx_opts *opts)
 	if (ctx.num_multi_pack_indexes_before == UINT32_MAX)
 		die(_("too many multi-pack-indexes"));
 
-	if (ctx.compact) {
-		struct multi_pack_index *m;
-
-		/*
-		 * Keep all MIDX layers excluding those in the range [from, to].
-		 */
-		for (m = ctx.base_midx; m; m = m->base_midx)
-			keep_hashes_nr++;
-		for (m = ctx.m;
-		     m && midx_hashcmp(m, ctx.compact_to, r->hash_algo);
-		     m = m->base_midx)
-			keep_hashes_nr++;
-
-		keep_hashes_nr++; /* include the compacted layer */
-	} else {
-		keep_hashes_nr = ctx.num_multi_pack_indexes_before + 1;
-	}
-	CALLOC_ARRAY(keep_hashes, keep_hashes_nr);
+	if (!is_lock_file_locked(&lk))
+		printf("%s\n", hash_to_hex_algop(midx_hash, r->hash_algo));
+	else if (opts->flags & MIDX_WRITE_NO_CHAIN)
+		BUG("lockfile held with MIDX_WRITE_NO_CHAIN set?");
 
 	if (ctx.incremental) {
-		FILE *chainf = fdopen_lock_file(&lk, "w");
 		struct strbuf final_midx_name = STRBUF_INIT;
 		struct multi_pack_index *m = ctx.base_midx;
+		struct multi_pack_index **layers = NULL;
+		size_t layers_nr = 0, layers_alloc = 0;
 
-		if (!chainf) {
-			error_errno(_("unable to open multi-pack-index chain file"));
-			goto cleanup;
+		if (is_lock_file_locked(&lk)){
+			FILE *chainf = fdopen_lock_file(&lk, "w");
+			if (!chainf) {
+				error_errno(_("unable to open multi-pack-index chain file"));
+				goto cleanup;
+			}
 		}
 
 		if (link_midx_to_chain(ctx.base_midx) < 0)
@@ -1747,60 +1765,64 @@ static int write_midx_internal(struct write_midx_opts *opts)
 		strbuf_release(&final_midx_name);
 
 		if (ctx.compact) {
-			struct multi_pack_index *m;
-			uint32_t num_layers_before_from = 0;
-			uint32_t i;
+			struct multi_pack_index *mp;
 
-			for (m = ctx.base_midx; m; m = m->base_midx)
-				num_layers_before_from++;
-
-			m = ctx.base_midx;
-			for (i = 0; i < num_layers_before_from; i++) {
-				uint32_t j = num_layers_before_from - i - 1;
-
-				keep_hashes[j] = xstrdup(midx_get_checksum_hex(m));
-				m = m->base_midx;
+			for (mp = ctx.base_midx; mp; mp = mp->base_midx) {
+				ALLOC_GROW(layers, layers_nr + 1, layers_alloc);
+				layers[layers_nr++] = mp;
 			}
+			while (layers_nr)
+				strvec_push(&keep_hashes,
+					    midx_get_checksum_hex(layers[--layers_nr]));
 
-			keep_hashes[i] = xstrdup(hash_to_hex_algop(midx_hash,
-								   r->hash_algo));
+			strvec_push(&keep_hashes,
+				    hash_to_hex_algop(midx_hash,
+						     r->hash_algo));
 
-			i = 0;
-			for (m = ctx.m;
-			     m && midx_hashcmp(m, ctx.compact_to, r->hash_algo);
-			     m = m->base_midx) {
-				keep_hashes[keep_hashes_nr - i - 1] =
-					xstrdup(midx_get_checksum_hex(m));
-				i++;
+			for (mp = ctx.m;
+			     mp && midx_hashcmp(mp, ctx.compact_to,
+						r->hash_algo);
+			     mp = mp->base_midx) {
+				ALLOC_GROW(layers, layers_nr + 1, layers_alloc);
+				layers[layers_nr++] = mp;
 			}
+			while (layers_nr)
+				strvec_push(&keep_hashes,
+					    midx_get_checksum_hex(layers[--layers_nr]));
 		} else {
-			keep_hashes[ctx.num_multi_pack_indexes_before] =
-				xstrdup(hash_to_hex_algop(midx_hash,
-							  r->hash_algo));
-
-			for (uint32_t i = 0; i < ctx.num_multi_pack_indexes_before; i++) {
-				uint32_t j = ctx.num_multi_pack_indexes_before - i - 1;
-
-				keep_hashes[j] = xstrdup(midx_get_checksum_hex(m));
-				m = m->base_midx;
+			for (; m; m = m->base_midx) {
+				ALLOC_GROW(layers, layers_nr + 1, layers_alloc);
+				layers[layers_nr++] = m;
 			}
+			while (layers_nr)
+				strvec_push(&keep_hashes,
+					    midx_get_checksum_hex(layers[--layers_nr]));
+
+			strvec_push(&keep_hashes,
+				    hash_to_hex_algop(midx_hash,
+						     r->hash_algo));
 		}
 
-		for (uint32_t i = 0; i < keep_hashes_nr; i++)
-			fprintf(get_lock_file_fp(&lk), "%s\n", keep_hashes[i]);
+		free(layers);
+
+		if (is_lock_file_locked(&lk))
+			for (size_t i = 0; i < keep_hashes.nr; i++)
+				fprintf(get_lock_file_fp(&lk), "%s\n",
+					keep_hashes.v[i]);
 	} else {
-		keep_hashes[ctx.num_multi_pack_indexes_before] =
-			xstrdup(hash_to_hex_algop(midx_hash, r->hash_algo));
+		strvec_push(&keep_hashes,
+			    hash_to_hex_algop(midx_hash, r->hash_algo));
 	}
 
 	if (ctx.m || ctx.base_midx)
 		odb_close(ctx.repo->objects);
 
-	if (commit_lock_file(&lk) < 0)
-		die_errno(_("could not write multi-pack-index"));
+	if (is_lock_file_locked(&lk)) {
+		if (commit_lock_file(&lk) < 0)
+			die_errno(_("could not write multi-pack-index"));
 
-	clear_midx_files(opts->source, keep_hashes, keep_hashes_nr,
-			 ctx.incremental);
+		clear_midx_files(opts->source, &keep_hashes, ctx.incremental);
+	}
 	result = 0;
 
 cleanup:
@@ -1816,11 +1838,7 @@ static int write_midx_internal(struct write_midx_opts *opts)
 	free(ctx.entries);
 	free(ctx.pack_perm);
 	free(ctx.pack_order);
-	if (keep_hashes) {
-		for (uint32_t i = 0; i < keep_hashes_nr; i++)
-			free((char *)keep_hashes[i]);
-		free(keep_hashes);
-	}
+	strvec_clear(&keep_hashes);
 	strbuf_release(&midx_name);
 	close_midx(midx_to_free);
 
@@ -1831,7 +1849,8 @@ static int write_midx_internal(struct write_midx_opts *opts)
 
 int write_midx_file(struct odb_source *source,
 		    const char *preferred_pack_name,
-		    const char *refs_snapshot, unsigned flags)
+		    const char *refs_snapshot,
+		    unsigned flags)
 {
 	struct write_midx_opts opts = {
 		.source = source,
@@ -1846,13 +1865,16 @@ int write_midx_file(struct odb_source *source,
 int write_midx_file_only(struct odb_source *source,
 			 struct string_list *packs_to_include,
 			 const char *preferred_pack_name,
-			 const char *refs_snapshot, unsigned flags)
+			 const char *refs_snapshot,
+			 const char *incremental_base,
+			 unsigned flags)
 {
 	struct write_midx_opts opts = {
 		.source = source,
 		.packs_to_include = packs_to_include,
 		.preferred_pack_name = preferred_pack_name,
 		.refs_snapshot = refs_snapshot,
+		.incremental_base = incremental_base,
 		.flags = flags,
 	};
 
@@ -1862,12 +1884,14 @@ int write_midx_file_only(struct odb_source *source,
 int write_midx_file_compact(struct odb_source *source,
 			    struct multi_pack_index *from,
 			    struct multi_pack_index *to,
+			    const char *incremental_base,
 			    unsigned flags)
 {
 	struct write_midx_opts opts = {
 		.source = source,
 		.compact_from = from,
 		.compact_to = to,
+		.incremental_base = incremental_base,
 		.flags = flags | MIDX_WRITE_COMPACT,
 	};
 
diff --git a/midx.c b/midx.c
index 81d6ab1..efbfbb1 100644
--- a/midx.c
+++ b/midx.c
@@ -12,6 +12,7 @@
 #include "chunk-format.h"
 #include "pack-bitmap.h"
 #include "pack-revindex.h"
+#include "strvec.h"
 
 #define MIDX_PACK_ERROR ((void *)(intptr_t)-1)
 
@@ -19,8 +20,7 @@ int midx_checksum_valid(struct multi_pack_index *m);
 void clear_midx_files_ext(struct odb_source *source, const char *ext,
 			  const char *keep_hash);
 void clear_incremental_midx_files_ext(struct odb_source *source, const char *ext,
-				      char **keep_hashes,
-				      uint32_t hashes_nr);
+				      const struct strvec *keep_hashes);
 int cmp_idx_or_pack_name(const char *idx_or_pack_name,
 			 const char *idx_name);
 
@@ -667,8 +667,8 @@ static int midx_pack_names_cmp(const void *a, const void *b, void *m_)
 		      m->pack_names[*(const size_t *)b]);
 }
 
-static int midx_contains_pack_1(struct multi_pack_index *m,
-				const char *idx_or_pack_name)
+int midx_layer_contains_pack(struct multi_pack_index *m,
+			     const char *idx_or_pack_name)
 {
 	uint32_t first = 0, last = m->num_packs;
 
@@ -709,7 +709,7 @@ static int midx_contains_pack_1(struct multi_pack_index *m,
 int midx_contains_pack(struct multi_pack_index *m, const char *idx_or_pack_name)
 {
 	for (; m; m = m->base_midx)
-		if (midx_contains_pack_1(m, idx_or_pack_name))
+		if (midx_layer_contains_pack(m, idx_or_pack_name))
 			return 1;
 	return 0;
 }
@@ -758,8 +758,7 @@ int midx_checksum_valid(struct multi_pack_index *m)
 }
 
 struct clear_midx_data {
-	char **keep;
-	uint32_t keep_nr;
+	struct strset keep;
 	const char *ext;
 };
 
@@ -767,15 +766,12 @@ static void clear_midx_file_ext(const char *full_path, size_t full_path_len UNUS
 				const char *file_name, void *_data)
 {
 	struct clear_midx_data *data = _data;
-	uint32_t i;
 
 	if (!(starts_with(file_name, "multi-pack-index-") &&
 	      ends_with(file_name, data->ext)))
 		return;
-	for (i = 0; i < data->keep_nr; i++) {
-		if (!strcmp(data->keep[i], file_name))
-			return;
-	}
+	if (strset_contains(&data->keep, file_name))
+		return;
 	if (unlink(full_path))
 		die_errno(_("failed to remove %s"), full_path);
 }
@@ -783,48 +779,49 @@ static void clear_midx_file_ext(const char *full_path, size_t full_path_len UNUS
 void clear_midx_files_ext(struct odb_source *source, const char *ext,
 			  const char *keep_hash)
 {
-	struct clear_midx_data data;
-	memset(&data, 0, sizeof(struct clear_midx_data));
+	struct clear_midx_data data = {
+		.keep = STRSET_INIT,
+		.ext = ext,
+	};
 
 	if (keep_hash) {
-		ALLOC_ARRAY(data.keep, 1);
+		struct strbuf buf = STRBUF_INIT;
+		strbuf_addf(&buf, "multi-pack-index-%s.%s", keep_hash, ext);
 
-		data.keep[0] = xstrfmt("multi-pack-index-%s.%s", keep_hash, ext);
-		data.keep_nr = 1;
+		strset_add(&data.keep, buf.buf);
+
+		strbuf_release(&buf);
 	}
-	data.ext = ext;
 
-	for_each_file_in_pack_dir(source->path,
-				  clear_midx_file_ext,
-				  &data);
+	for_each_file_in_pack_dir(source->path, clear_midx_file_ext, &data);
 
-	if (keep_hash)
-		free(data.keep[0]);
-	free(data.keep);
+	strset_clear(&data.keep);
 }
 
 void clear_incremental_midx_files_ext(struct odb_source *source, const char *ext,
-				      char **keep_hashes,
-				      uint32_t hashes_nr)
+				      const struct strvec *keep_hashes)
 {
-	struct clear_midx_data data;
-	uint32_t i;
+	struct clear_midx_data data = {
+		.keep = STRSET_INIT,
+		.ext = ext,
+	};
+	struct strbuf buf = STRBUF_INIT;
 
-	memset(&data, 0, sizeof(struct clear_midx_data));
+	if (keep_hashes) {
+		for (size_t i = 0; i < keep_hashes->nr; i++) {
+			strbuf_reset(&buf);
+			strbuf_addf(&buf, "multi-pack-index-%s.%s",
+				    keep_hashes->v[i], ext);
 
-	ALLOC_ARRAY(data.keep, hashes_nr);
-	for (i = 0; i < hashes_nr; i++)
-		data.keep[i] = xstrfmt("multi-pack-index-%s.%s", keep_hashes[i],
-				       ext);
-	data.keep_nr = hashes_nr;
-	data.ext = ext;
+			strset_add(&data.keep, buf.buf);
+		}
+	}
 
 	for_each_file_in_pack_subdir(source->path, "multi-pack-index.d",
 				     clear_midx_file_ext, &data);
 
-	for (i = 0; i < hashes_nr; i++)
-		free(data.keep[i]);
-	free(data.keep);
+	strbuf_release(&buf);
+	strset_clear(&data.keep);
 }
 
 void clear_midx_file(struct repository *r)
@@ -853,6 +850,35 @@ void clear_midx_file(struct repository *r)
 	strbuf_release(&midx);
 }
 
+void clear_incremental_midx_files(struct repository *r,
+				  const struct strvec *keep_hashes)
+{
+	struct odb_source *source = r->objects->sources;
+	struct strbuf chain = STRBUF_INIT;
+
+	get_midx_chain_filename(source, &chain);
+
+	for (; source; source = source->next) {
+		struct odb_source_files *files = odb_source_files_downcast(source);
+		if (files->packed->midx)
+			close_midx(files->packed->midx);
+		files->packed->midx = NULL;
+	}
+
+	if (!keep_hashes && remove_path(chain.buf))
+		die(_("failed to clear multi-pack-index chain at %s"),
+		    chain.buf);
+
+	clear_incremental_midx_files_ext(r->objects->sources, MIDX_EXT_BITMAP,
+					 keep_hashes);
+	clear_incremental_midx_files_ext(r->objects->sources, MIDX_EXT_REV,
+					 keep_hashes);
+	clear_incremental_midx_files_ext(r->objects->sources, MIDX_EXT_MIDX,
+					 keep_hashes);
+
+	strbuf_release(&chain);
+}
+
 static int verify_midx_error;
 
 __attribute__((format (printf, 1, 2)))
diff --git a/midx.h b/midx.h
index 08f3728..63853a0 100644
--- a/midx.h
+++ b/midx.h
@@ -9,6 +9,7 @@ struct repository;
 struct bitmapped_pack;
 struct git_hash_algo;
 struct odb_source;
+struct strvec;
 
 #define MIDX_SIGNATURE 0x4d494458 /* "MIDX" */
 #define MIDX_VERSION_V1 1
@@ -83,6 +84,7 @@ struct multi_pack_index {
 #define MIDX_WRITE_BITMAP_LOOKUP_TABLE (1 << 4)
 #define MIDX_WRITE_INCREMENTAL (1 << 5)
 #define MIDX_WRITE_COMPACT (1 << 6)
+#define MIDX_WRITE_NO_CHAIN (1 << 7)
 
 #define MIDX_EXT_REV "rev"
 #define MIDX_EXT_BITMAP "bitmap"
@@ -118,6 +120,8 @@ struct object_id *nth_midxed_object_oid(struct object_id *oid,
 int fill_midx_entry(struct multi_pack_index *m, const struct object_id *oid, struct pack_entry *e);
 int midx_contains_pack(struct multi_pack_index *m,
 		       const char *idx_or_pack_name);
+int midx_layer_contains_pack(struct multi_pack_index *m,
+			     const char *idx_or_pack_name);
 int midx_preferred_pack(struct multi_pack_index *m, uint32_t *pack_int_id);
 int prepare_multi_pack_index_one(struct odb_source *source);
 
@@ -131,12 +135,17 @@ int write_midx_file(struct odb_source *source,
 int write_midx_file_only(struct odb_source *source,
 			 struct string_list *packs_to_include,
 			 const char *preferred_pack_name,
-			 const char *refs_snapshot, unsigned flags);
+			 const char *refs_snapshot,
+			 const char *incremental_base,
+			 unsigned flags);
 int write_midx_file_compact(struct odb_source *source,
 			    struct multi_pack_index *from,
 			    struct multi_pack_index *to,
+			    const char *incremental_base,
 			    unsigned flags);
 void clear_midx_file(struct repository *r);
+void clear_incremental_midx_files(struct repository *r,
+				  const struct strvec *keep_hashes);
 int verify_midx_file(struct odb_source *source, unsigned flags);
 int expire_midx_packs(struct odb_source *source, unsigned flags);
 int midx_repack(struct odb_source *source, size_t batch_size, unsigned flags);
diff --git a/negotiator/default.c b/negotiator/default.c
index 3cac047..78d58d5 100644
--- a/negotiator/default.c
+++ b/negotiator/default.c
@@ -175,6 +175,13 @@ static int ack(struct fetch_negotiator *n, struct commit *c)
 	return known_to_be_common;
 }
 
+static void have_sent(struct fetch_negotiator *n, struct commit *c)
+{
+	if (repo_parse_commit(the_repository, c))
+		return;
+	mark_common(n->data, c, 0, 0);
+}
+
 static void release(struct fetch_negotiator *n)
 {
 	clear_prio_queue(&((struct negotiation_state *)n->data)->rev_list);
@@ -188,6 +195,7 @@ void default_negotiator_init(struct fetch_negotiator *negotiator)
 	negotiator->add_tip = add_tip;
 	negotiator->next = next;
 	negotiator->ack = ack;
+	negotiator->have_sent = have_sent;
 	negotiator->release = release;
 	negotiator->data = CALLOC_ARRAY(ns, 1);
 	ns->rev_list.compare = compare_commits_by_commit_date;
diff --git a/negotiator/noop.c b/negotiator/noop.c
index 65e3c20..edf1b45 100644
--- a/negotiator/noop.c
+++ b/negotiator/noop.c
@@ -29,6 +29,12 @@ static int ack(struct fetch_negotiator *n UNUSED, struct commit *c UNUSED)
 	return 0;
 }
 
+static void have_sent(struct fetch_negotiator *n UNUSED,
+		      struct commit *c UNUSED)
+{
+	/* nothing to do */
+}
+
 static void release(struct fetch_negotiator *n UNUSED)
 {
 	/* nothing to release */
@@ -40,6 +46,7 @@ void noop_negotiator_init(struct fetch_negotiator *negotiator)
 	negotiator->add_tip = add_tip;
 	negotiator->next = next;
 	negotiator->ack = ack;
+	negotiator->have_sent = have_sent;
 	negotiator->release = release;
 	negotiator->data = NULL;
 }
diff --git a/negotiator/skipping.c b/negotiator/skipping.c
index fe4126c..68c9b3b 100644
--- a/negotiator/skipping.c
+++ b/negotiator/skipping.c
@@ -243,6 +243,13 @@ static int ack(struct fetch_negotiator *n, struct commit *c)
 	return known_to_be_common;
 }
 
+static void have_sent(struct fetch_negotiator *n, struct commit *c)
+{
+	if (repo_parse_commit(the_repository, c))
+		return;
+	mark_common(n->data, c);
+}
+
 static void release(struct fetch_negotiator *n)
 {
 	struct data *data = n->data;
@@ -259,6 +266,7 @@ void skipping_negotiator_init(struct fetch_negotiator *negotiator)
 	negotiator->add_tip = add_tip;
 	negotiator->next = next;
 	negotiator->ack = ack;
+	negotiator->have_sent = have_sent;
 	negotiator->release = release;
 	negotiator->data = CALLOC_ARRAY(data, 1);
 	data->rev_list.compare = compare;
diff --git a/notes.c b/notes.c
index 51a7ef9..8f315e2 100644
--- a/notes.c
+++ b/notes.c
@@ -796,7 +796,7 @@ static int prune_notes_helper(const struct object_id *object_oid,
 	struct note_delete_list *n;
 
 	if (odb_has_object(the_repository->objects, object_oid,
-			   HAS_OBJECT_RECHECK_PACKED | HAS_OBJECT_FETCH_PROMISOR))
+			   ODB_HAS_OBJECT_RECHECK_PACKED | ODB_HAS_OBJECT_FETCH_PROMISOR))
 		return 0; /* nothing to do for this note */
 
 	/* failed to find object => prune this note */
diff --git a/object-file.c b/object-file.c
index f0b029f..90f995d 100644
--- a/object-file.c
+++ b/object-file.c
@@ -21,6 +21,7 @@
 #include "object-file.h"
 #include "odb.h"
 #include "odb/streaming.h"
+#include "odb/transaction.h"
 #include "oidtree.h"
 #include "pack.h"
 #include "packfile.h"
@@ -33,6 +34,9 @@
 /* The maximum size for an object header. */
 #define MAX_HEADER_LEN 32
 
+static struct oidtree *odb_source_loose_cache(struct odb_source *source,
+					      const struct object_id *oid);
+
 static int get_conv_flags(unsigned flags)
 {
 	if (flags & INDEX_RENORMALIZE)
@@ -906,7 +910,7 @@ static int start_loose_object_common(struct odb_source *source,
 
 	fd = create_tmpfile(source->odb->repo, tmp_file, filename);
 	if (fd < 0) {
-		if (flags & WRITE_OBJECT_SILENT)
+		if (flags & ODB_WRITE_OBJECT_SILENT)
 			return -1;
 		else if (errno == EACCES)
 			return error(_("insufficient permission for adding "
@@ -1039,7 +1043,7 @@ static int write_loose_object(struct odb_source *source,
 		utb.actime = mtime;
 		utb.modtime = mtime;
 		if (utime(tmp_file.buf, &utb) < 0 &&
-		    !(flags & WRITE_OBJECT_SILENT))
+		    !(flags & ODB_WRITE_OBJECT_SILENT))
 			warning_errno(_("failed utime() on %s"), tmp_file.buf);
 	}
 
@@ -1065,6 +1069,7 @@ int odb_source_loose_write_stream(struct odb_source *source,
 	struct git_hash_ctx c, compat_c;
 	struct strbuf tmp_file = STRBUF_INIT;
 	struct strbuf filename = STRBUF_INIT;
+	unsigned char buf[8192];
 	int dirlen;
 	char hdr[MAX_HEADER_LEN];
 	int hdrlen;
@@ -1097,9 +1102,17 @@ int odb_source_loose_write_stream(struct odb_source *source,
 		unsigned char *in0 = stream.next_in;
 
 		if (!stream.avail_in && !in_stream->is_finished) {
-			const void *in = in_stream->read(in_stream, &stream.avail_in);
-			stream.next_in = (void *)in;
-			in0 = (unsigned char *)in;
+			ssize_t read_len = odb_write_stream_read(in_stream, buf,
+								 sizeof(buf));
+			if (read_len < 0) {
+				close(fd);
+				err = -1;
+				goto cleanup;
+			}
+
+			stream.avail_in = read_len;
+			stream.next_in = buf;
+			in0 = buf;
 			/* All data has been read. */
 			if (in_stream->is_finished)
 				flush = 1;
@@ -1115,7 +1128,7 @@ int odb_source_loose_write_stream(struct odb_source *source,
 	} while (ret == Z_OK || ret == Z_BUF_ERROR);
 
 	if (stream.total_in != len + hdrlen)
-		die(_("write stream object %ld != %"PRIuMAX), stream.total_in,
+		die(_("write stream object %"PRIuMAX" != %"PRIuMAX), (uintmax_t)stream.total_in,
 		    (uintmax_t)len + hdrlen);
 
 	/*
@@ -1166,7 +1179,8 @@ int odb_source_loose_write_stream(struct odb_source *source,
 int odb_source_loose_write_object(struct odb_source *source,
 				  const void *buf, unsigned long len,
 				  enum object_type type, struct object_id *oid,
-				  struct object_id *compat_oid_in, unsigned flags)
+				  struct object_id *compat_oid_in,
+				  enum odb_write_object_flags flags)
 {
 	const struct git_hash_algo *algo = source->odb->repo->hash_algo;
 	const struct git_hash_algo *compat = source->odb->repo->compat_hash_algo;
@@ -1279,8 +1293,9 @@ static int index_mem(struct index_state *istate,
 		}
 	}
 	if (flags & INDEX_FORMAT_CHECK) {
-		struct fsck_options opts = FSCK_OPTIONS_DEFAULT;
+		struct fsck_options opts;
 
+		fsck_options_init(&opts, the_repository, FSCK_OPTIONS_DEFAULT);
 		opts.strict = 1;
 		opts.error_func = hash_format_check_report;
 		if (fsck_buffer(null_oid(istate->repo->hash_algo), type, buf, size, &opts))
@@ -1374,7 +1389,7 @@ static int already_written(struct odb_transaction_files *transaction,
 {
 	/* The object may already exist in the repository */
 	if (odb_has_object(transaction->base.source->odb, oid,
-			   HAS_OBJECT_RECHECK_PACKED | HAS_OBJECT_FETCH_PROMISOR))
+			   ODB_HAS_OBJECT_RECHECK_PACKED | ODB_HAS_OBJECT_FETCH_PROMISOR))
 		return 1;
 
 	/* Might want to keep the list sorted */
@@ -1387,11 +1402,10 @@ static int already_written(struct odb_transaction_files *transaction,
 }
 
 /* Lazily create backing packfile for the state */
-static void prepare_packfile_transaction(struct odb_transaction_files *transaction,
-					 unsigned flags)
+static void prepare_packfile_transaction(struct odb_transaction_files *transaction)
 {
 	struct transaction_packfile *state = &transaction->packfile;
-	if (!(flags & INDEX_WRITE_OBJECT) || state->f)
+	if (state->f)
 		return;
 
 	state->f = create_tmp_packfile(transaction->base.source->odb->repo,
@@ -1404,33 +1418,53 @@ static void prepare_packfile_transaction(struct odb_transaction_files *transacti
 		die_errno("unable to write pack header");
 }
 
+static int hash_blob_stream(struct odb_write_stream *stream,
+			    const struct git_hash_algo *hash_algo,
+			    struct object_id *result_oid, size_t size)
+{
+	unsigned char buf[16384];
+	struct git_hash_ctx ctx;
+	unsigned header_len;
+	size_t bytes_hashed = 0;
+
+	header_len = format_object_header((char *)buf, sizeof(buf),
+					  OBJ_BLOB, size);
+	hash_algo->init_fn(&ctx);
+	git_hash_update(&ctx, buf, header_len);
+
+	while (!stream->is_finished) {
+		ssize_t read_result = odb_write_stream_read(stream, buf,
+							    sizeof(buf));
+
+		if (read_result < 0)
+			return -1;
+
+		git_hash_update(&ctx, buf, read_result);
+		bytes_hashed += read_result;
+	}
+
+	if (bytes_hashed != size)
+		return -1;
+
+	git_hash_final_oid(result_oid, &ctx);
+
+	return 0;
+}
+
 /*
- * Read the contents from fd for size bytes, streaming it to the
- * packfile in state while updating the hash in ctx. Signal a failure
- * by returning a negative value when the resulting pack would exceed
- * the pack size limit and this is not the first object in the pack,
- * so that the caller can discard what we wrote from the current pack
- * by truncating it and opening a new one. The caller will then call
- * us again after rewinding the input fd.
- *
- * The already_hashed_to pointer is kept untouched by the caller to
- * make sure we do not hash the same byte when we are called
- * again. This way, the caller does not have to checkpoint its hash
- * status before calling us just in case we ask it to call us again
- * with a new pack.
+ * Read the contents from the stream provided, streaming it to the
+ * packfile in state while updating the hash in ctx.
  */
-static int stream_blob_to_pack(struct transaction_packfile *state,
-			       struct git_hash_ctx *ctx, off_t *already_hashed_to,
-			       int fd, size_t size, const char *path,
-			       unsigned flags)
+static void stream_blob_to_pack(struct transaction_packfile *state,
+				struct git_hash_ctx *ctx, size_t size,
+				struct odb_write_stream *stream)
 {
 	git_zstream s;
 	unsigned char ibuf[16384];
 	unsigned char obuf[16384];
 	unsigned hdrlen;
 	int status = Z_OK;
-	int write_object = (flags & INDEX_WRITE_OBJECT);
-	off_t offset = 0;
+	size_t bytes_read = 0;
 
 	git_deflate_init(&s, pack_compression_level);
 
@@ -1439,45 +1473,27 @@ static int stream_blob_to_pack(struct transaction_packfile *state,
 	s.avail_out = sizeof(obuf) - hdrlen;
 
 	while (status != Z_STREAM_END) {
-		if (size && !s.avail_in) {
-			size_t rsize = size < sizeof(ibuf) ? size : sizeof(ibuf);
-			ssize_t read_result = read_in_full(fd, ibuf, rsize);
-			if (read_result < 0)
-				die_errno("failed to read from '%s'", path);
-			if ((size_t)read_result != rsize)
-				die("failed to read %u bytes from '%s'",
-				    (unsigned)rsize, path);
-			offset += rsize;
-			if (*already_hashed_to < offset) {
-				size_t hsize = offset - *already_hashed_to;
-				if (rsize < hsize)
-					hsize = rsize;
-				if (hsize)
-					git_hash_update(ctx, ibuf, hsize);
-				*already_hashed_to = offset;
-			}
+		if (!stream->is_finished && !s.avail_in) {
+			ssize_t rsize = odb_write_stream_read(stream, ibuf,
+							      sizeof(ibuf));
+
+			if (rsize < 0)
+				die("failed to read blob data");
+
+			git_hash_update(ctx, ibuf, rsize);
+
 			s.next_in = ibuf;
 			s.avail_in = rsize;
-			size -= rsize;
+			bytes_read += rsize;
 		}
 
-		status = git_deflate(&s, size ? 0 : Z_FINISH);
+		status = git_deflate(&s, stream->is_finished ? Z_FINISH : 0);
 
 		if (!s.avail_out || status == Z_STREAM_END) {
-			if (write_object) {
-				size_t written = s.next_out - obuf;
+			size_t written = s.next_out - obuf;
 
-				/* would we bust the size limit? */
-				if (state->nr_written &&
-				    pack_size_limit_cfg &&
-				    pack_size_limit_cfg < state->offset + written) {
-					git_deflate_abort(&s);
-					return -1;
-				}
-
-				hashwrite(state->f, obuf, written);
-				state->offset += written;
-			}
+			hashwrite(state->f, obuf, written);
+			state->offset += written;
 			s.next_out = obuf;
 			s.avail_out = sizeof(obuf);
 		}
@@ -1491,8 +1507,12 @@ static int stream_blob_to_pack(struct transaction_packfile *state,
 			die("unexpected deflate failure: %d", status);
 		}
 	}
+
+	if (bytes_read != size)
+		die("read %" PRIuMAX " bytes of blob data, but expected %" PRIuMAX " bytes",
+		    (uintmax_t)bytes_read, (uintmax_t)size);
+
 	git_deflate_end(&s);
-	return 0;
 }
 
 static void flush_packfile_transaction(struct odb_transaction_files *transaction)
@@ -1563,64 +1583,49 @@ static void flush_packfile_transaction(struct odb_transaction_files *transaction
  * binary blobs, they generally do not want to get any conversion, and
  * callers should avoid this code path when filters are requested.
  */
-static int index_blob_packfile_transaction(struct odb_transaction_files *transaction,
-					   struct object_id *result_oid, int fd,
-					   size_t size, const char *path,
-					   unsigned flags)
+static int odb_transaction_files_write_object_stream(struct odb_transaction *base,
+						     struct odb_write_stream *stream,
+						     size_t size,
+						     struct object_id *result_oid)
 {
+	struct odb_transaction_files *transaction = container_of(base,
+								 struct odb_transaction_files,
+								 base);
 	struct transaction_packfile *state = &transaction->packfile;
-	off_t seekback, already_hashed_to;
 	struct git_hash_ctx ctx;
 	unsigned char obuf[16384];
 	unsigned header_len;
 	struct hashfile_checkpoint checkpoint;
-	struct pack_idx_entry *idx = NULL;
-
-	seekback = lseek(fd, 0, SEEK_CUR);
-	if (seekback == (off_t)-1)
-		return error("cannot find the current offset");
+	struct pack_idx_entry *idx;
 
 	header_len = format_object_header((char *)obuf, sizeof(obuf),
 					  OBJ_BLOB, size);
 	transaction->base.source->odb->repo->hash_algo->init_fn(&ctx);
 	git_hash_update(&ctx, obuf, header_len);
 
-	/* Note: idx is non-NULL when we are writing */
-	if ((flags & INDEX_WRITE_OBJECT) != 0) {
-		CALLOC_ARRAY(idx, 1);
-
-		prepare_packfile_transaction(transaction, flags);
-		hashfile_checkpoint_init(state->f, &checkpoint);
-	}
-
-	already_hashed_to = 0;
-
-	while (1) {
-		prepare_packfile_transaction(transaction, flags);
-		if (idx) {
-			hashfile_checkpoint(state->f, &checkpoint);
-			idx->offset = state->offset;
-			crc32_begin(state->f);
-		}
-		if (!stream_blob_to_pack(state, &ctx, &already_hashed_to,
-					 fd, size, path, flags))
-			break;
-		/*
-		 * Writing this object to the current pack will make
-		 * it too big; we need to truncate it, start a new
-		 * pack, and write into it.
-		 */
-		if (!idx)
-			BUG("should not happen");
-		hashfile_truncate(state->f, &checkpoint);
-		state->offset = checkpoint.offset;
+	/*
+	 * If writing another object to the packfile could result in it
+	 * exceeding the configured size limit, flush the current packfile
+	 * transaction.
+	 *
+	 * Note that this uses the inflated object size as an approximation.
+	 * Blob objects written in this manner are not delta-compressed, so
+	 * the difference between the inflated and on-disk size is limited
+	 * to zlib compression and is sufficient for this check.
+	 */
+	if (state->nr_written && pack_size_limit_cfg &&
+	    pack_size_limit_cfg < state->offset + size)
 		flush_packfile_transaction(transaction);
-		if (lseek(fd, seekback, SEEK_SET) == (off_t)-1)
-			return error("cannot seek back");
-	}
+
+	CALLOC_ARRAY(idx, 1);
+	prepare_packfile_transaction(transaction);
+	hashfile_checkpoint_init(state->f, &checkpoint);
+
+	hashfile_checkpoint(state->f, &checkpoint);
+	idx->offset = state->offset;
+	crc32_begin(state->f);
+	stream_blob_to_pack(state, &ctx, size, stream);
 	git_hash_final_oid(result_oid, &ctx);
-	if (!idx)
-		return 0;
 
 	idx->crc32 = crc32_end(state->f);
 	if (already_written(transaction, result_oid)) {
@@ -1658,18 +1663,25 @@ int index_fd(struct index_state *istate, struct object_id *oid,
 		ret = index_core(istate, oid, fd, xsize_t(st->st_size),
 				 type, path, flags);
 	} else {
-		struct object_database *odb = the_repository->objects;
-		struct odb_transaction_files *files_transaction;
-		struct odb_transaction *transaction;
+		struct odb_write_stream stream;
+		odb_write_stream_from_fd(&stream, fd, xsize_t(st->st_size));
 
-		transaction = odb_transaction_begin(odb);
-		files_transaction = container_of(odb->transaction,
-						 struct odb_transaction_files,
-						 base);
-		ret = index_blob_packfile_transaction(files_transaction, oid, fd,
-						      xsize_t(st->st_size),
-						      path, flags);
-		odb_transaction_commit(transaction);
+		if (flags & INDEX_WRITE_OBJECT) {
+			struct object_database *odb = the_repository->objects;
+			struct odb_transaction *transaction = odb_transaction_begin(odb);
+
+			ret = odb_transaction_write_object_stream(odb->transaction,
+								  &stream,
+								  xsize_t(st->st_size),
+								  oid);
+			odb_transaction_commit(transaction);
+		} else {
+			ret = hash_blob_stream(&stream,
+					       the_repository->hash_algo, oid,
+					       xsize_t(st->st_size));
+		}
+
+		odb_write_stream_release(&stream);
 	}
 
 	close(fd);
@@ -1845,11 +1857,29 @@ static int for_each_object_wrapper_cb(const struct object_id *oid,
 	}
 }
 
+static int for_each_prefixed_object_wrapper_cb(const struct object_id *oid,
+					       void *node_data UNUSED,
+					       void *cb_data)
+{
+	struct for_each_object_wrapper_data *data = cb_data;
+	if (data->request) {
+		struct object_info oi = *data->request;
+
+		if (odb_source_loose_read_object_info(data->source,
+						      oid, &oi, 0) < 0)
+			return -1;
+
+		return data->cb(oid, &oi, data->cb_data);
+	} else {
+		return data->cb(oid, NULL, data->cb_data);
+	}
+}
+
 int odb_source_loose_for_each_object(struct odb_source *source,
 				     const struct object_info *request,
 				     odb_for_each_object_cb cb,
 				     void *cb_data,
-				     unsigned flags)
+				     const struct odb_for_each_object_options *opts)
 {
 	struct for_each_object_wrapper_data data = {
 		.source = source,
@@ -1859,11 +1889,16 @@ int odb_source_loose_for_each_object(struct odb_source *source,
 	};
 
 	/* There are no loose promisor objects, so we can return immediately. */
-	if ((flags & ODB_FOR_EACH_OBJECT_PROMISOR_ONLY))
+	if ((opts->flags & ODB_FOR_EACH_OBJECT_PROMISOR_ONLY))
 		return 0;
-	if ((flags & ODB_FOR_EACH_OBJECT_LOCAL_ONLY) && !source->local)
+	if ((opts->flags & ODB_FOR_EACH_OBJECT_LOCAL_ONLY) && !source->local)
 		return 0;
 
+	if (opts->prefix)
+		return oidtree_each(odb_source_loose_cache(source, opts->prefix),
+				    opts->prefix, opts->prefix_hex_len,
+				    for_each_prefixed_object_wrapper_cb, &data);
+
 	return for_each_loose_file_in_source(source, for_each_object_wrapper_cb,
 					     NULL, NULL, &data);
 }
@@ -1914,9 +1949,10 @@ int odb_source_loose_count_objects(struct odb_source *source,
 		*out = count * 256;
 		ret = 0;
 	} else {
+		struct odb_for_each_object_options opts = { 0 };
 		*out = 0;
 		ret = odb_source_loose_for_each_object(source, NULL, count_loose_object,
-						       out, 0);
+						       out, &opts);
 	}
 
 out:
@@ -1926,16 +1962,54 @@ int odb_source_loose_count_objects(struct odb_source *source,
 	return ret;
 }
 
+struct find_abbrev_len_data {
+	const struct object_id *oid;
+	unsigned len;
+};
+
+static int find_abbrev_len_cb(const struct object_id *oid,
+			      struct object_info *oi UNUSED,
+			      void *cb_data)
+{
+	struct find_abbrev_len_data *data = cb_data;
+	unsigned len = oid_common_prefix_hexlen(oid, data->oid);
+	if (len != hash_algos[oid->algo].hexsz && len >= data->len)
+		data->len = len + 1;
+	return 0;
+}
+
+int odb_source_loose_find_abbrev_len(struct odb_source *source,
+				     const struct object_id *oid,
+				     unsigned min_len,
+				     unsigned *out)
+{
+	struct odb_for_each_object_options opts = {
+		.prefix = oid,
+		.prefix_hex_len = min_len,
+	};
+	struct find_abbrev_len_data data = {
+		.oid = oid,
+		.len = min_len,
+	};
+	int ret;
+
+	ret = odb_source_loose_for_each_object(source, NULL, find_abbrev_len_cb,
+					       &data, &opts);
+	*out = data.len;
+
+	return ret;
+}
+
 static int append_loose_object(const struct object_id *oid,
 			       const char *path UNUSED,
 			       void *data)
 {
-	oidtree_insert(data, oid);
+	oidtree_insert(data, oid, NULL);
 	return 0;
 }
 
-struct oidtree *odb_source_loose_cache(struct odb_source *source,
-				       const struct object_id *oid)
+static struct oidtree *odb_source_loose_cache(struct odb_source *source,
+					      const struct object_id *oid)
 {
 	struct odb_source_files *files = odb_source_files_downcast(source);
 	int subdir_nr = oid->hash[0];
@@ -2126,6 +2200,7 @@ struct odb_transaction *odb_transaction_files_begin(struct odb_source *source)
 	transaction = xcalloc(1, sizeof(*transaction));
 	transaction->base.source = source;
 	transaction->base.commit = odb_transaction_files_commit;
+	transaction->base.write_object_stream = odb_transaction_files_write_object_stream;
 
 	return &transaction->base;
 }
@@ -2227,6 +2302,7 @@ int odb_source_loose_read_object_stream(struct odb_read_stream **out,
 	struct object_info oi = OBJECT_INFO_INIT;
 	struct odb_loose_read_stream *st;
 	unsigned long mapsize;
+	unsigned long size_ul;
 	void *mapped;
 
 	mapped = odb_source_loose_map_object(source, oid, &mapsize);
@@ -2250,11 +2326,18 @@ int odb_source_loose_read_object_stream(struct odb_read_stream **out,
 		goto error;
 	}
 
-	oi.sizep = &st->base.size;
+	/*
+	 * object_info.sizep is unsigned long* (32-bit on Windows), but
+	 * st->base.size is size_t (64-bit). Use temporary variable.
+	 * Note: loose objects >4GB would still truncate here, but such
+	 * large loose objects are uncommon (they'd normally be packed).
+	 */
+	oi.sizep = &size_ul;
 	oi.typep = &st->base.type;
 
 	if (parse_loose_header(st->hdr, &oi) < 0 || st->base.type < 0)
 		goto error;
+	st->base.size = size_ul;
 
 	st->mapped = mapped;
 	st->mapsize = mapsize;
diff --git a/object-file.h b/object-file.h
index f8d8805..5241b8d 100644
--- a/object-file.h
+++ b/object-file.h
@@ -68,20 +68,14 @@ int odb_source_loose_freshen_object(struct odb_source *source,
 int odb_source_loose_write_object(struct odb_source *source,
 				  const void *buf, unsigned long len,
 				  enum object_type type, struct object_id *oid,
-				  struct object_id *compat_oid_in, unsigned flags);
+				  struct object_id *compat_oid_in,
+				  enum odb_write_object_flags flags);
 
 int odb_source_loose_write_stream(struct odb_source *source,
 				  struct odb_write_stream *stream, size_t len,
 				  struct object_id *oid);
 
 /*
- * Populate and return the loose object cache array corresponding to the
- * given object ID.
- */
-struct oidtree *odb_source_loose_cache(struct odb_source *source,
-				       const struct object_id *oid);
-
-/*
  * Put in `buf` the name of the file in the local object database that
  * would be used to store a loose object with the specified oid.
  */
@@ -137,7 +131,7 @@ int odb_source_loose_for_each_object(struct odb_source *source,
 				     const struct object_info *request,
 				     odb_for_each_object_cb cb,
 				     void *cb_data,
-				     unsigned flags);
+				     const struct odb_for_each_object_options *opts);
 
 /*
  * Count the number of loose objects in this source.
@@ -153,6 +147,18 @@ int odb_source_loose_count_objects(struct odb_source *source,
 				   enum odb_count_objects_flags flags,
 				   unsigned long *out);
 
+/*
+ * Find the shortest unique prefix for the given object ID, where `min_len` is
+ * the minimum length that the prefix should have.
+ *
+ * Returns 0 on success, in which case the computed length will be written to
+ * `out`. Otherwise, a negative error code is returned.
+ */
+int odb_source_loose_find_abbrev_len(struct odb_source *source,
+				     const struct object_id *oid,
+				     unsigned min_len,
+				     unsigned *out);
+
 /**
  * format_object_header() is a thin wrapper around s xsnprintf() that
  * writes the initial "<type> <obj-len>" part of the loose object
diff --git a/object-name.c b/object-name.c
index 84a34b8..9ac86f1 100644
--- a/object-name.c
+++ b/object-name.c
@@ -15,11 +15,9 @@
 #include "refs.h"
 #include "remote.h"
 #include "dir.h"
+#include "odb.h"
 #include "oid-array.h"
-#include "oidtree.h"
-#include "packfile.h"
 #include "pretty.h"
-#include "object-file.h"
 #include "read-cache-ll.h"
 #include "repo-settings.h"
 #include "repository.h"
@@ -49,30 +47,29 @@ struct disambiguate_state {
 	unsigned candidate_ok:1;
 	unsigned disambiguate_fn_used:1;
 	unsigned ambiguous:1;
-	unsigned always_call_fn:1;
 };
 
-static void update_candidates(struct disambiguate_state *ds, const struct object_id *current)
+static int update_disambiguate_state(const struct object_id *current,
+				     struct object_info *oi UNUSED,
+				     void *cb_data)
 {
+	struct disambiguate_state *ds = cb_data;
+
 	/* The hash algorithm of current has already been filtered */
-	if (ds->always_call_fn) {
-		ds->ambiguous = ds->fn(ds->repo, current, ds->cb_data) ? 1 : 0;
-		return;
-	}
 	if (!ds->candidate_exists) {
 		/* this is the first candidate */
 		oidcpy(&ds->candidate, current);
 		ds->candidate_exists = 1;
-		return;
+		return 0;
 	} else if (oideq(&ds->candidate, current)) {
 		/* the same as what we already have seen */
-		return;
+		return 0;
 	}
 
 	if (!ds->fn) {
 		/* cannot disambiguate between ds->candidate and current */
 		ds->ambiguous = 1;
-		return;
+		return ds->ambiguous;
 	}
 
 	if (!ds->candidate_checked) {
@@ -85,7 +82,7 @@ static void update_candidates(struct disambiguate_state *ds, const struct object
 		/* discard the candidate; we know it does not satisfy fn */
 		oidcpy(&ds->candidate, current);
 		ds->candidate_checked = 0;
-		return;
+		return 0;
 	}
 
 	/* if we reach this point, we know ds->candidate satisfies fn */
@@ -96,128 +93,12 @@ static void update_candidates(struct disambiguate_state *ds, const struct object
 		 */
 		ds->candidate_ok = 0;
 		ds->ambiguous = 1;
+		return ds->ambiguous;
 	}
 
 	/* otherwise, current can be discarded and candidate is still good */
-}
 
-static int match_hash(unsigned, const unsigned char *, const unsigned char *);
-
-static enum cb_next match_prefix(const struct object_id *oid, void *arg)
-{
-	struct disambiguate_state *ds = arg;
-	/* no need to call match_hash, oidtree_each did prefix match */
-	update_candidates(ds, oid);
-	return ds->ambiguous ? CB_BREAK : CB_CONTINUE;
-}
-
-static void find_short_object_filename(struct disambiguate_state *ds)
-{
-	struct odb_source *source;
-
-	for (source = ds->repo->objects->sources; source && !ds->ambiguous; source = source->next)
-		oidtree_each(odb_source_loose_cache(source, &ds->bin_pfx),
-				&ds->bin_pfx, ds->len, match_prefix, ds);
-}
-
-static int match_hash(unsigned len, const unsigned char *a, const unsigned char *b)
-{
-	do {
-		if (*a != *b)
-			return 0;
-		a++;
-		b++;
-		len -= 2;
-	} while (len > 1);
-	if (len)
-		if ((*a ^ *b) & 0xf0)
-			return 0;
-	return 1;
-}
-
-static void unique_in_midx(struct multi_pack_index *m,
-			   struct disambiguate_state *ds)
-{
-	for (; m; m = m->base_midx) {
-		uint32_t num, i, first = 0;
-		const struct object_id *current = NULL;
-		int len = ds->len > ds->repo->hash_algo->hexsz ?
-			ds->repo->hash_algo->hexsz : ds->len;
-
-		if (!m->num_objects)
-			continue;
-
-		num = m->num_objects + m->num_objects_in_base;
-
-		bsearch_one_midx(&ds->bin_pfx, m, &first);
-
-		/*
-		 * At this point, "first" is the location of the lowest
-		 * object with an object name that could match
-		 * "bin_pfx".  See if we have 0, 1 or more objects that
-		 * actually match(es).
-		 */
-		for (i = first; i < num && !ds->ambiguous; i++) {
-			struct object_id oid;
-			current = nth_midxed_object_oid(&oid, m, i);
-			if (!match_hash(len, ds->bin_pfx.hash, current->hash))
-				break;
-			update_candidates(ds, current);
-		}
-	}
-}
-
-static void unique_in_pack(struct packed_git *p,
-			   struct disambiguate_state *ds)
-{
-	uint32_t num, i, first = 0;
-	int len = ds->len > ds->repo->hash_algo->hexsz ?
-		ds->repo->hash_algo->hexsz : ds->len;
-
-	if (p->multi_pack_index)
-		return;
-
-	if (open_pack_index(p) || !p->num_objects)
-		return;
-
-	num = p->num_objects;
-	bsearch_pack(&ds->bin_pfx, p, &first);
-
-	/*
-	 * At this point, "first" is the location of the lowest object
-	 * with an object name that could match "bin_pfx".  See if we have
-	 * 0, 1 or more objects that actually match(es).
-	 */
-	for (i = first; i < num && !ds->ambiguous; i++) {
-		struct object_id oid;
-		nth_packed_object_id(&oid, p, i);
-		if (!match_hash(len, ds->bin_pfx.hash, oid.hash))
-			break;
-		update_candidates(ds, &oid);
-	}
-}
-
-static void find_short_packed_object(struct disambiguate_state *ds)
-{
-	struct odb_source *source;
-	struct packed_git *p;
-
-	/* Skip, unless oids from the storage hash algorithm are wanted */
-	if (ds->bin_pfx.algo && (&hash_algos[ds->bin_pfx.algo] != ds->repo->hash_algo))
-		return;
-
-	odb_prepare_alternates(ds->repo->objects);
-	for (source = ds->repo->objects->sources; source && !ds->ambiguous; source = source->next) {
-		struct multi_pack_index *m = get_multi_pack_index(source);
-		if (m)
-			unique_in_midx(m, ds);
-	}
-
-	repo_for_each_pack(ds->repo, p) {
-		if (ds->ambiguous)
-			break;
-		unique_in_pack(p, ds);
-	}
+	return 0;
 }
 
 static int finish_object_disambiguation(struct disambiguate_state *ds,
@@ -348,41 +229,57 @@ int set_disambiguate_hint_config(const char *var, const char *value)
 	return error("unknown hint type for '%s': %s", var, value);
 }
 
+static int parse_oid_prefix(const char *name, int len,
+			    const struct git_hash_algo *algo,
+			    char *hex_out,
+			    struct object_id *oid_out)
+{
+	for (int i = 0; i < len; i++) {
+		unsigned char c = name[i];
+		unsigned char val;
+		if (c >= '0' && c <= '9') {
+			val = c - '0';
+		} else if (c >= 'a' && c <= 'f') {
+			val = c - 'a' + 10;
+		} else if (c >= 'A' && c <='F') {
+			val = c - 'A' + 10;
+			c -= 'A' - 'a';
+		} else {
+			return -1;
+		}
+
+		if (hex_out)
+			hex_out[i] = c;
+		if (oid_out) {
+			if (!(i & 1))
+				val <<= 4;
+			oid_out->hash[i >> 1] |= val;
+		}
+	}
+
+	if (hex_out)
+		hex_out[len] = '\0';
+	if (oid_out)
+		oid_out->algo = algo ? hash_algo_by_ptr(algo) : GIT_HASH_UNKNOWN;
+
+	return 0;
+}
+
 static int init_object_disambiguation(struct repository *r,
 				      const char *name, int len,
 				      const struct git_hash_algo *algo,
 				      struct disambiguate_state *ds)
 {
-	int i;
-
 	if (len < MINIMUM_ABBREV || len > GIT_MAX_HEXSZ)
 		return -1;
 
 	memset(ds, 0, sizeof(*ds));
 
-	for (i = 0; i < len ;i++) {
-		unsigned char c = name[i];
-		unsigned char val;
-		if (c >= '0' && c <= '9')
-			val = c - '0';
-		else if (c >= 'a' && c <= 'f')
-			val = c - 'a' + 10;
-		else if (c >= 'A' && c <='F') {
-			val = c - 'A' + 10;
-			c -= 'A' - 'a';
-		}
-		else
-			return -1;
-		ds->hex_pfx[i] = c;
-		if (!(i & 1))
-			val <<= 4;
-		ds->bin_pfx.hash[i >> 1] |= val;
-	}
+	if (parse_oid_prefix(name, len, algo, ds->hex_pfx, &ds->bin_pfx) < 0)
+		return -1;
 
 	ds->len = len;
-	ds->hex_pfx[len] = '\0';
 	ds->repo = r;
-	ds->bin_pfx.algo = algo ? hash_algo_by_ptr(algo) : GIT_HASH_UNKNOWN;
 	odb_prepare_alternates(r->objects);
 	return 0;
 }
@@ -510,8 +407,8 @@ static int collect_ambiguous(const struct object_id *oid, void *data)
 	return 0;
 }
 
-static int repo_collect_ambiguous(struct repository *r UNUSED,
-				  const struct object_id *oid,
+static int repo_collect_ambiguous(const struct object_id *oid,
+				  struct object_info *oi UNUSED,
 				  void *data)
 {
 	return collect_ambiguous(oid, data);
@@ -561,6 +458,7 @@ static enum get_oid_result get_short_oid(struct repository *r,
 					 struct object_id *oid,
 					 unsigned flags)
 {
+	struct odb_for_each_object_options opts = { 0 };
 	int status;
 	struct disambiguate_state ds;
 	int quietly = !!(flags & GET_OID_QUIETLY);
@@ -588,8 +486,11 @@ static enum get_oid_result get_short_oid(struct repository *r,
 	else
 		ds.fn = default_disambiguate_hint;
 
-	find_short_object_filename(&ds);
-	find_short_packed_object(&ds);
+	opts.prefix = &ds.bin_pfx;
+	opts.prefix_hex_len = ds.len;
+
+	odb_for_each_object_ext(r->objects, NULL, update_disambiguate_state,
+				&ds, &opts);
 	status = finish_object_disambiguation(&ds, oid);
 
 	/*
@@ -599,8 +500,8 @@ static enum get_oid_result get_short_oid(struct repository *r,
 	 */
 	if (status == MISSING_OBJECT) {
 		odb_reprepare(r->objects);
-		find_short_object_filename(&ds);
-		find_short_packed_object(&ds);
+		odb_for_each_object_ext(r->objects, NULL, update_disambiguate_state,
+					&ds, &opts);
 		status = finish_object_disambiguation(&ds, oid);
 	}
 
@@ -648,169 +549,25 @@ int repo_for_each_abbrev(struct repository *r, const char *prefix,
 			 const struct git_hash_algo *algo,
 			 each_abbrev_fn fn, void *cb_data)
 {
+	struct object_id prefix_oid = { 0 };
+	struct odb_for_each_object_options opts = {
+		.prefix = &prefix_oid,
+		.prefix_hex_len = strlen(prefix),
+	};
 	struct oid_array collect = OID_ARRAY_INIT;
-	struct disambiguate_state ds;
 	int ret;
 
-	if (init_object_disambiguation(r, prefix, strlen(prefix), algo, &ds) < 0)
+	if (parse_oid_prefix(prefix, opts.prefix_hex_len, algo, NULL, &prefix_oid) < 0)
 		return -1;
 
-	ds.always_call_fn = 1;
-	ds.fn = repo_collect_ambiguous;
-	ds.cb_data = &collect;
-	find_short_object_filename(&ds);
-	find_short_packed_object(&ds);
+	if (odb_for_each_object_ext(r->objects, NULL, repo_collect_ambiguous, &collect, &opts) < 0)
+		return -1;
 
 	ret = oid_array_for_each_unique(&collect, fn, cb_data);
 	oid_array_clear(&collect);
 	return ret;
 }
 
-/*
- * Return the slot of the most-significant bit set in "val". There are various
- * ways to do this quickly with fls() or __builtin_clzl(), but speed is
- * probably not a big deal here.
- */
-static unsigned msb(unsigned long val)
-{
-	unsigned r = 0;
-	while (val >>= 1)
-		r++;
-	return r;
-}
-
-struct min_abbrev_data {
-	unsigned int init_len;
-	unsigned int cur_len;
-	char *hex;
-	struct repository *repo;
-	const struct object_id *oid;
-};
-
-static inline char get_hex_char_from_oid(const struct object_id *oid,
-					 unsigned int pos)
-{
-	static const char hex[] = "0123456789abcdef";
-
-	if ((pos & 1) == 0)
-		return hex[oid->hash[pos >> 1] >> 4];
-	else
-		return hex[oid->hash[pos >> 1] & 0xf];
-}
-
-static int extend_abbrev_len(const struct object_id *oid,
-			     struct min_abbrev_data *mad)
-{
-	unsigned int i = mad->init_len;
-	while (mad->hex[i] && mad->hex[i] == get_hex_char_from_oid(oid, i))
-		i++;
-
-	if (mad->hex[i] && i >= mad->cur_len)
-		mad->cur_len = i + 1;
-
-	return 0;
-}
-
-static int repo_extend_abbrev_len(struct repository *r UNUSED,
-				  const struct object_id *oid,
-				  void *cb_data)
-{
-	return extend_abbrev_len(oid, cb_data);
-}
-
-static void find_abbrev_len_for_midx(struct multi_pack_index *m,
-				     struct min_abbrev_data *mad)
-{
-	for (; m; m = m->base_midx) {
-		int match = 0;
-		uint32_t num, first = 0;
-		struct object_id oid;
-		const struct object_id *mad_oid;
-
-		if (!m->num_objects)
-			continue;
-
-		num = m->num_objects + m->num_objects_in_base;
-		mad_oid = mad->oid;
-		match = bsearch_one_midx(mad_oid, m, &first);
-
-		/*
-		 * first is now the position in the packfile where we
-		 * would insert mad->hash if it does not exist (or the
-		 * position of mad->hash if it does exist). Hence, we
-		 * consider a maximum of two objects nearby for the
-		 * abbreviation length.
-		 */
-		mad->init_len = 0;
-		if (!match) {
-			if (nth_midxed_object_oid(&oid, m, first))
-				extend_abbrev_len(&oid, mad);
-		} else if (first < num - 1) {
-			if (nth_midxed_object_oid(&oid, m, first + 1))
-				extend_abbrev_len(&oid, mad);
-		}
-		if (first > 0) {
-			if (nth_midxed_object_oid(&oid, m, first - 1))
-				extend_abbrev_len(&oid, mad);
-		}
-		mad->init_len = mad->cur_len;
-	}
-}
-
-static void find_abbrev_len_for_pack(struct packed_git *p,
-				     struct min_abbrev_data *mad)
-{
-	int match = 0;
-	uint32_t num, first = 0;
-	struct object_id oid;
-	const struct object_id *mad_oid;
-
-	if (p->multi_pack_index)
-		return;
-
-	if (open_pack_index(p) || !p->num_objects)
-		return;
-
-	num = p->num_objects;
-	mad_oid = mad->oid;
-	match = bsearch_pack(mad_oid, p, &first);
-
-	/*
-	 * first is now the position in the packfile where we would insert
-	 * mad->hash if it does not exist (or the position of mad->hash if
-	 * it does exist). Hence, we consider a maximum of two objects
-	 * nearby for the abbreviation length.
-	 */
-	mad->init_len = 0;
-	if (!match) {
-		if (!nth_packed_object_id(&oid, p, first))
-			extend_abbrev_len(&oid, mad);
-	} else if (first < num - 1) {
-		if (!nth_packed_object_id(&oid, p, first + 1))
-			extend_abbrev_len(&oid, mad);
-	}
-	if (first > 0) {
-		if (!nth_packed_object_id(&oid, p, first - 1))
-			extend_abbrev_len(&oid, mad);
-	}
-	mad->init_len = mad->cur_len;
-}
-
-static void find_abbrev_len_packed(struct min_abbrev_data *mad)
-{
-	struct packed_git *p;
-
-	odb_prepare_alternates(mad->repo->objects);
-	for (struct odb_source *source = mad->repo->objects->sources; source; source = source->next) {
-		struct multi_pack_index *m = get_multi_pack_index(source);
-		if (m)
-			find_abbrev_len_for_midx(m, mad);
-	}
-
-	repo_for_each_pack(mad->repo, p)
-		find_abbrev_len_for_pack(p, mad);
-}
-
 void strbuf_repo_add_unique_abbrev(struct strbuf *sb, struct repository *repo,
 				   const struct object_id *oid, int abbrev_len)
 {
@@ -827,65 +584,19 @@ void strbuf_add_unique_abbrev(struct strbuf *sb, const struct object_id *oid,
 }
 
 int repo_find_unique_abbrev_r(struct repository *r, char *hex,
-			      const struct object_id *oid, int len)
+			      const struct object_id *oid, int min_len)
 {
 	const struct git_hash_algo *algo =
 		oid->algo ? &hash_algos[oid->algo] : r->hash_algo;
-	struct disambiguate_state ds;
-	struct min_abbrev_data mad;
-	struct object_id oid_ret;
-	const unsigned hexsz = algo->hexsz;
+	unsigned len;
 
-	if (len < 0) {
-		unsigned long count;
-
-		if (odb_count_objects(r->objects, ODB_COUNT_OBJECTS_APPROXIMATE, &count) < 0)
-			count = 0;
-
-		/*
-		 * Add one because the MSB only tells us the highest bit set,
-		 * not including the value of all the _other_ bits (so "15"
-		 * is only one off of 2^4, but the MSB is the 3rd bit.
-		 */
-		len = msb(count) + 1;
-		/*
-		 * We now know we have on the order of 2^len objects, which
-		 * expects a collision at 2^(len/2). But we also care about hex
-		 * chars, not bits, and there are 4 bits per hex. So all
-		 * together we need to divide by 2 and round up.
-		 */
-		len = DIV_ROUND_UP(len, 2);
-		/*
-		 * For very small repos, we stick with our regular fallback.
-		 */
-		if (len < FALLBACK_DEFAULT_ABBREV)
-			len = FALLBACK_DEFAULT_ABBREV;
-	}
+	if (odb_find_abbrev_len(r->objects, oid, min_len, &len) < 0)
+		len = algo->hexsz;
 
 	oid_to_hex_r(hex, oid);
-	if (len >= hexsz || !len)
-		return hexsz;
+	hex[len] = 0;
 
-	mad.repo = r;
-	mad.init_len = len;
-	mad.cur_len = len;
-	mad.hex = hex;
-	mad.oid = oid;
-
-	find_abbrev_len_packed(&mad);
-
-	if (init_object_disambiguation(r, hex, mad.cur_len, algo, &ds) < 0)
-		return -1;
-
-	ds.fn = repo_extend_abbrev_len;
-	ds.always_call_fn = 1;
-	ds.cb_data = (void *)&mad;
-
-	find_short_object_filename(&ds);
-	(void)finish_object_disambiguation(&ds, &oid_ret);
-
-	hex[mad.cur_len] = 0;
-	return mad.cur_len;
+	return len;
 }
 
 const char *repo_find_unique_abbrev(struct repository *r,
@@ -1992,11 +1703,11 @@ static char *resolve_relative_path(struct repository *r, const char *rel)
 	if (!starts_with(rel, "./") && !starts_with(rel, "../"))
 		return NULL;
 
-	if (r != the_repository || !is_inside_work_tree())
+	if (r != the_repository || !is_inside_work_tree(the_repository))
 		die(_("relative path syntax can't be used outside working tree"));
 
 	/* die() inside prefix_path() if resolved path is outside worktree */
-	return prefix_path(startup_info->prefix,
+	return prefix_path(the_repository, startup_info->prefix,
 			   startup_info->prefix ? strlen(startup_info->prefix) : 0,
 			   rel);
 }
diff --git a/object.h b/object.h
index d814647..8fb03ff 100644
--- a/object.h
+++ b/object.h
@@ -67,6 +67,7 @@ void object_array_init(struct object_array *array);
  * revision.h:               0---------10         15               23--------28
  * fetch-pack.c:             01    67
  * negotiator/default.c:       2--5
+ * negotiator/skipping.c:      2--5
  * walker.c:                 0-2
  * upload-pack.c:                4       11-----14  16-----19
  * builtin/blame.c:                        12-13
@@ -74,15 +75,15 @@ void object_array_init(struct object_array *array);
  * bundle.c:                                        16
  * http-push.c:                          11-----14
  * commit-graph.c:                                15
- * commit-reach.c:                                  16-----19
+ * commit-reach.c:                                  16-------20
  * builtin/last-modified.c:                         1617
- * sha1-name.c:                                              20
+ * object-name.c:                                            20
  * list-objects-filter.c:                                      21
  * bloom.c:                                                    2122
  * builtin/fsck.c:           0--3
  * builtin/index-pack.c:                                     2021
  * reflog.c:                           10--12
- * builtin/show-branch.c:    0-------------------------------------------26
+ * builtin/show-branch.c:    0-----------------------------------------------28
  * builtin/unpack-objects.c:                                 2021
  * pack-bitmap.h:                                              2122
  */
diff --git a/odb.c b/odb.c
index 350e23f..965ef68 100644
--- a/odb.c
+++ b/odb.c
@@ -12,7 +12,9 @@
 #include "midx.h"
 #include "object-file-convert.h"
 #include "object-file.h"
+#include "object-name.h"
 #include "odb.h"
+#include "odb/source-inmemory.h"
 #include "packfile.h"
 #include "path.h"
 #include "promisor-remote.h"
@@ -30,40 +32,6 @@
 KHASH_INIT(odb_path_map, const char * /* key: odb_path */,
 	struct odb_source *, 1, fspathhash, fspatheq)
 
-/*
- * This is meant to hold a *small* number of objects that you would
- * want odb_read_object() to be able to return, but yet you do not want
- * to write them into the object store (e.g. a browse-only
- * application).
- */
-struct cached_object_entry {
-	struct object_id oid;
-	struct cached_object {
-		enum object_type type;
-		const void *buf;
-		unsigned long size;
-	} value;
-};
-
-static const struct cached_object *find_cached_object(struct object_database *object_store,
-						      const struct object_id *oid)
-{
-	static const struct cached_object empty_tree = {
-		.type = OBJ_TREE,
-		.buf = "",
-	};
-	const struct cached_object_entry *co = object_store->cached_objects;
-
-	for (size_t i = 0; i < object_store->cached_object_nr; i++, co++)
-		if (oideq(&co->oid, oid))
-			return &co->value;
-
-	if (oid->algo && oideq(oid, hash_algos[oid->algo].empty_tree))
-		return &empty_tree;
-
-	return NULL;
-}
-
 int odb_mkstemp(struct object_database *odb,
 		struct strbuf *temp_filename, const char *pattern)
 {
@@ -583,7 +551,6 @@ static int do_oid_object_info_extended(struct object_database *odb,
 				       const struct object_id *oid,
 				       struct object_info *oi, unsigned flags)
 {
-	const struct cached_object *co;
 	const struct object_id *real = oid;
 	int already_retried = 0;
 
@@ -593,25 +560,8 @@ static int do_oid_object_info_extended(struct object_database *odb,
 	if (is_null_oid(real))
 		return -1;
 
-	co = find_cached_object(odb, real);
-	if (co) {
-		if (oi) {
-			if (oi->typep)
-				*(oi->typep) = co->type;
-			if (oi->sizep)
-				*(oi->sizep) = co->size;
-			if (oi->disk_sizep)
-				*(oi->disk_sizep) = 0;
-			if (oi->delta_base_oid)
-				oidclr(oi->delta_base_oid, odb->repo->hash_algo);
-			if (oi->contentp)
-				*oi->contentp = xmemdupz(co->buf, co->size);
-			if (oi->mtimep)
-				*oi->mtimep = 0;
-			oi->whence = OI_CACHED;
-		}
+	if (!odb_source_read_object_info(odb->inmemory_objects, oid, oi, flags))
 		return 0;
-	}
 
 	odb_prepare_alternates(odb);
 
@@ -783,24 +733,12 @@ int odb_pretend_object(struct object_database *odb,
 		       void *buf, unsigned long len, enum object_type type,
 		       struct object_id *oid)
 {
-	struct cached_object_entry *co;
-	char *co_buf;
-
 	hash_object_file(odb->repo->hash_algo, buf, len, type, oid);
-	if (odb_has_object(odb, oid, 0) ||
-	    find_cached_object(odb, oid))
+	if (odb_has_object(odb, oid, 0))
 		return 0;
 
-	ALLOC_GROW(odb->cached_objects,
-		   odb->cached_object_nr + 1, odb->cached_object_alloc);
-	co = &odb->cached_objects[odb->cached_object_nr++];
-	co->value.size = len;
-	co->value.type = type;
-	co_buf = xmalloc(len);
-	memcpy(co_buf, buf, len);
-	co->value.buf = co_buf;
-	oidcpy(&co->oid, oid);
-	return 0;
+	return odb_source_write_object(odb->inmemory_objects,
+				       buf, len, type, oid, NULL, 0);
 }
 
 void *odb_read_object(struct object_database *odb,
@@ -871,15 +809,15 @@ void *odb_read_object_peeled(struct object_database *odb,
 }
 
 int odb_has_object(struct object_database *odb, const struct object_id *oid,
-		   enum has_object_flags flags)
+		   enum odb_has_object_flags flags)
 {
 	unsigned object_info_flags = 0;
 
 	if (!startup_info->have_repository)
 		return 0;
-	if (!(flags & HAS_OBJECT_RECHECK_PACKED))
+	if (!(flags & ODB_HAS_OBJECT_RECHECK_PACKED))
 		object_info_flags |= OBJECT_INFO_QUICK;
-	if (!(flags & HAS_OBJECT_FETCH_PROMISOR))
+	if (!(flags & ODB_HAS_OBJECT_FETCH_PROMISOR))
 		object_info_flags |= OBJECT_INFO_SKIP_FETCH_OBJECT;
 
 	return odb_read_object_info_extended(odb, oid, NULL, object_info_flags) >= 0;
@@ -896,20 +834,20 @@ int odb_freshen_object(struct object_database *odb,
 	return 0;
 }
 
-int odb_for_each_object(struct object_database *odb,
-			const struct object_info *request,
-			odb_for_each_object_cb cb,
-			void *cb_data,
-			unsigned flags)
+int odb_for_each_object_ext(struct object_database *odb,
+			    const struct object_info *request,
+			    odb_for_each_object_cb cb,
+			    void *cb_data,
+			    const struct odb_for_each_object_options *opts)
 {
 	int ret;
 
 	odb_prepare_alternates(odb);
 	for (struct odb_source *source = odb->sources; source; source = source->next) {
-		if (flags & ODB_FOR_EACH_OBJECT_LOCAL_ONLY && !source->local)
+		if (opts->flags & ODB_FOR_EACH_OBJECT_LOCAL_ONLY && !source->local)
 			continue;
 
-		ret = odb_source_for_each_object(source, request, cb, cb_data, flags);
+		ret = odb_source_for_each_object(source, request, cb, cb_data, opts);
 		if (ret)
 			return ret;
 	}
@@ -917,6 +855,18 @@ int odb_for_each_object(struct object_database *odb,
 	return 0;
 }
 
+int odb_for_each_object(struct object_database *odb,
+			const struct object_info *request,
+			odb_for_each_object_cb cb,
+			void *cb_data,
+			enum odb_for_each_object_flags flags)
+{
+	struct odb_for_each_object_options opts = {
+		.flags = flags,
+	};
+	return odb_for_each_object_ext(odb, request, cb, cb_data, &opts);
+}
+
 int odb_count_objects(struct object_database *odb,
 		      enum odb_count_objects_flags flags,
 		      unsigned long *out)
@@ -952,6 +902,78 @@ int odb_count_objects(struct object_database *odb,
 	return ret;
 }
 
+/*
+ * Return the slot of the most-significant bit set in "val". There are various
+ * ways to do this quickly with fls() or __builtin_clzl(), but speed is
+ * probably not a big deal here.
+ */
+static unsigned msb(unsigned long val)
+{
+	unsigned r = 0;
+	while (val >>= 1)
+		r++;
+	return r;
+}
+
+int odb_find_abbrev_len(struct object_database *odb,
+			const struct object_id *oid,
+			int min_length,
+			unsigned *out)
+{
+	const struct git_hash_algo *algo =
+		oid->algo ? &hash_algos[oid->algo] : odb->repo->hash_algo;
+	const unsigned hexsz = algo->hexsz;
+	unsigned len;
+	int ret;
+
+	if (min_length < 0) {
+		unsigned long count;
+
+		if (odb_count_objects(odb, ODB_COUNT_OBJECTS_APPROXIMATE, &count) < 0)
+			count = 0;
+
+		/*
+		 * Add one because the MSB only tells us the highest bit set,
+		 * not including the value of all the _other_ bits (so "15"
+		 * is only one off of 2^4, but the MSB is the 3rd bit.
+		 */
+		len = msb(count) + 1;
+		/*
+		 * We now know we have on the order of 2^len objects, which
+		 * expects a collision at 2^(len/2). But we also care about hex
+		 * chars, not bits, and there are 4 bits per hex. So all
+		 * together we need to divide by 2 and round up.
+		 */
+		len = DIV_ROUND_UP(len, 2);
+		/*
+		 * For very small repos, we stick with our regular fallback.
+		 */
+		if (len < FALLBACK_DEFAULT_ABBREV)
+			len = FALLBACK_DEFAULT_ABBREV;
+	} else {
+		len = min_length;
+	}
+
+	if (len >= hexsz || !len) {
+		*out = hexsz;
+		ret = 0;
+		goto out;
+	}
+
+	odb_prepare_alternates(odb);
+	for (struct odb_source *source = odb->sources; source; source = source->next) {
+		ret = odb_source_find_abbrev_len(source, oid, len, &len);
+		if (ret)
+			goto out;
+	}
+
+	ret = 0;
+	*out = len;
+
+out:
+	return ret;
+}
+
 void odb_assert_oid_type(struct object_database *odb,
 			 const struct object_id *oid, enum object_type expect)
 {
@@ -968,7 +990,7 @@ int odb_write_object_ext(struct object_database *odb,
 			 enum object_type type,
 			 struct object_id *oid,
 			 struct object_id *compat_oid,
-			 unsigned flags)
+			 enum odb_write_object_flags flags)
 {
 	return odb_source_write_object(odb->sources, buf, len, type,
 				       oid, compat_oid, flags);
@@ -998,6 +1020,7 @@ struct object_database *odb_new(struct repository *repo,
 	o->sources = odb_source_new(o, primary_source, true);
 	o->sources_tail = &o->sources->next;
 	o->alternate_db = xstrdup_or_null(secondary_sources);
+	o->inmemory_objects = &odb_source_inmemory_new(o)->base;
 
 	free(to_free);
 
@@ -1021,6 +1044,10 @@ static void odb_free_sources(struct object_database *o)
 		odb_source_free(o->sources);
 		o->sources = next;
 	}
+
+	odb_source_free(o->inmemory_objects);
+	o->inmemory_objects = NULL;
+
 	kh_destroy_odb_path_map(o->source_by_path);
 	o->source_by_path = NULL;
 }
@@ -1038,10 +1065,6 @@ void odb_free(struct object_database *o)
 	odb_close(o);
 	odb_free_sources(o);
 
-	for (size_t i = 0; i < o->cached_object_nr; i++)
-		free((char *) o->cached_objects[i].value.buf);
-	free(o->cached_objects);
-
 	string_list_clear(&o->submodule_source_paths, 0);
 
 	free(o);
@@ -1069,28 +1092,3 @@ void odb_reprepare(struct object_database *o)
 
 	obj_read_unlock();
 }
-
-struct odb_transaction *odb_transaction_begin(struct object_database *odb)
-{
-	if (odb->transaction)
-		return NULL;
-
-	odb->transaction = odb_transaction_files_begin(odb->sources);
-
-	return odb->transaction;
-}
-
-void odb_transaction_commit(struct odb_transaction *transaction)
-{
-	if (!transaction)
-		return;
-
-	/*
-	 * Ensure the transaction ending matches the pending transaction.
-	 */
-	ASSERT(transaction == transaction->source->odb->transaction);
-
-	transaction->commit(transaction);
-	transaction->source->odb->transaction = NULL;
-	free(transaction);
-}
diff --git a/odb.h b/odb.h
index 9aee260..73553ed 100644
--- a/odb.h
+++ b/odb.h
@@ -1,19 +1,18 @@
 #ifndef ODB_H
 #define ODB_H
 
-#include "hashmap.h"
 #include "object.h"
 #include "oidset.h"
 #include "oidmap.h"
 #include "string-list.h"
 #include "thread-utils.h"
 
-struct oidmap;
-struct oidtree;
+struct cached_object_entry;
+struct odb_source_inmemory;
+struct packed_git;
+struct repository;
 struct strbuf;
 struct strvec;
-struct repository;
-struct multi_pack_index;
 
 /*
  * Set this to 0 to prevent odb_read_object_info_extended() from fetching missing
@@ -31,28 +30,6 @@ extern int fetch_if_missing;
  */
 char *compute_alternate_path(const char *path, struct strbuf *err);
 
-struct packed_git;
-struct packfile_store;
-struct cached_object_entry;
-
-/*
- * A transaction may be started for an object database prior to writing new
- * objects via odb_transaction_begin(). These objects are not committed until
- * odb_transaction_commit() is invoked. Only a single transaction may be pending
- * at a time.
- *
- * Each ODB source is expected to implement its own transaction handling.
- */
-struct odb_transaction;
-typedef void (*odb_transaction_commit_fn)(struct odb_transaction *transaction);
-struct odb_transaction {
-	/* The ODB source the transaction is opened against. */
-	struct odb_source *source;
-
-	/* The ODB source specific callback invoked to commit a transaction. */
-	odb_transaction_commit_fn commit;
-};
-
 /*
  * The object database encapsulates access to objects in a repository. It
  * manages one or more sources that store the actual objects which are
@@ -104,8 +81,7 @@ struct object_database {
 	 * to write them into the object store (e.g. a browse-only
 	 * application).
 	 */
-	struct cached_object_entry *cached_objects;
-	size_t cached_object_nr, cached_object_alloc;
+	struct odb_source *inmemory_objects;
 
 	/*
 	 * A fast, rough count of the number of objects in the repository.
@@ -155,19 +131,6 @@ void odb_close(struct object_database *o);
 void odb_reprepare(struct object_database *o);
 
 /*
- * Starts an ODB transaction. Subsequent objects are written to the transaction
- * and not committed until odb_transaction_commit() is invoked on the
- * transaction. If the ODB already has a pending transaction, NULL is returned.
- */
-struct odb_transaction *odb_transaction_begin(struct object_database *odb);
-
-/*
- * Commits an ODB transaction making the written objects visible. If the
- * specified transaction is NULL, the function is a no-op.
- */
-void odb_transaction_commit(struct odb_transaction *transaction);
-
-/*
  * Find source by its object directory path. Returns a `NULL` pointer in case
  * the source could not be found.
  */
@@ -395,11 +358,11 @@ int odb_read_object_info(struct object_database *odb,
 			 const struct object_id *oid,
 			 unsigned long *sizep);
 
-enum has_object_flags {
+enum odb_has_object_flags {
 	/* Retry packed storage after checking packed and loose storage */
-	HAS_OBJECT_RECHECK_PACKED = (1 << 0),
+	ODB_HAS_OBJECT_RECHECK_PACKED = (1 << 0),
 	/* Allow fetching the object in case the repository has a promisor remote. */
-	HAS_OBJECT_FETCH_PROMISOR = (1 << 1),
+	ODB_HAS_OBJECT_FETCH_PROMISOR = (1 << 1),
 };
 
 /*
@@ -408,7 +371,7 @@ enum has_object_flags {
  */
 int odb_has_object(struct object_database *odb,
 		   const struct object_id *oid,
-		   enum has_object_flags flags);
+		   enum odb_has_object_flags flags);
 
 int odb_freshen_object(struct object_database *odb,
 		       const struct object_id *oid);
@@ -482,6 +445,22 @@ typedef int (*odb_for_each_object_cb)(const struct object_id *oid,
 				      void *cb_data);
 
 /*
+ * Options that can be passed to `odb_for_each_object()` and its
+ * backend-specific implementations.
+ */
+struct odb_for_each_object_options {
+	/* A bitfield of `odb_for_each_object_flags`. */
+	enum odb_for_each_object_flags flags;
+
+	/*
+	 * If set, only iterate through objects whose first `prefix_hex_len`
+	 * hex characters matches the given prefix.
+	 */
+	const struct object_id *prefix;
+	size_t prefix_hex_len;
+};
+
+/*
  * Iterate through all objects contained in the object database. Note that
  * objects may be iterated over multiple times in case they are either stored
  * in different backends or in case they are stored in multiple sources.
@@ -495,11 +474,18 @@ typedef int (*odb_for_each_object_cb)(const struct object_id *oid,
  * Returns 0 on success, a negative error code in case a failure occurred, or
  * an arbitrary non-zero error code returned by the callback itself.
  */
+int odb_for_each_object_ext(struct object_database *odb,
+			    const struct object_info *request,
+			    odb_for_each_object_cb cb,
+			    void *cb_data,
+			    const struct odb_for_each_object_options *opts);
+
+/* Same as `odb_for_each_object_ext()` with `opts.flags` set to the given flags. */
 int odb_for_each_object(struct object_database *odb,
 			const struct object_info *request,
 			odb_for_each_object_cb cb,
 			void *cb_data,
-			unsigned flags);
+			enum odb_for_each_object_flags flags);
 
 enum odb_count_objects_flags {
 	/*
@@ -522,19 +508,35 @@ int odb_count_objects(struct object_database *odb,
 		      enum odb_count_objects_flags flags,
 		      unsigned long *out);
 
-enum {
+/*
+ * Given an object ID, find the minimum required length required to make the
+ * object ID unique across the whole object database.
+ *
+ * The `min_len` determines the minimum abbreviated length that'll be returned
+ * by this function. If `min_len < 0`, then the function will set a sensible
+ * default minimum abbreviation length.
+ *
+ * Returns 0 on success, a negative error code otherwise. The computed length
+ * will be assigned to `*out`.
+ */
+int odb_find_abbrev_len(struct object_database *odb,
+			const struct object_id *oid,
+			int min_len,
+			unsigned *out);
+
+enum odb_write_object_flags {
 	/*
 	 * By default, `odb_write_object()` does not actually write anything
 	 * into the object store, but only computes the object ID. This flag
 	 * changes that so that the object will be written as a loose object
 	 * and persisted.
 	 */
-	WRITE_OBJECT_PERSIST = (1 << 0),
+	ODB_WRITE_OBJECT_PERSIST = (1 << 0),
 
 	/*
 	 * Do not print an error in case something goes wrong.
 	 */
-	WRITE_OBJECT_SILENT = (1 << 1),
+	ODB_WRITE_OBJECT_SILENT = (1 << 1),
 };
 
 /*
@@ -550,7 +552,7 @@ int odb_write_object_ext(struct object_database *odb,
 			 enum object_type type,
 			 struct object_id *oid,
 			 struct object_id *compat_oid,
-			 unsigned flags);
+			 enum odb_write_object_flags flags);
 
 static inline int odb_write_object(struct object_database *odb,
 				   const void *buf, unsigned long len,
@@ -560,11 +562,7 @@ static inline int odb_write_object(struct object_database *odb,
 	return odb_write_object_ext(odb, buf, len, type, oid, NULL, 0);
 }
 
-struct odb_write_stream {
-	const void *(*read)(struct odb_write_stream *, unsigned long *len);
-	void *data;
-	int is_finished;
-};
+struct odb_write_stream;
 
 int odb_write_object_stream(struct object_database *odb,
 			    struct odb_write_stream *stream, size_t len,
diff --git a/odb/source-files.c b/odb/source-files.c
index c08d899..b5abd20 100644
--- a/odb/source-files.c
+++ b/odb/source-files.c
@@ -75,18 +75,18 @@ static int odb_source_files_for_each_object(struct odb_source *source,
 					    const struct object_info *request,
 					    odb_for_each_object_cb cb,
 					    void *cb_data,
-					    unsigned flags)
+					    const struct odb_for_each_object_options *opts)
 {
 	struct odb_source_files *files = odb_source_files_downcast(source);
 	int ret;
 
-	if (!(flags & ODB_FOR_EACH_OBJECT_PROMISOR_ONLY)) {
-		ret = odb_source_loose_for_each_object(source, request, cb, cb_data, flags);
+	if (!(opts->flags & ODB_FOR_EACH_OBJECT_PROMISOR_ONLY)) {
+		ret = odb_source_loose_for_each_object(source, request, cb, cb_data, opts);
 		if (ret)
 			return ret;
 	}
 
-	ret = packfile_store_for_each_object(files->packed, request, cb, cb_data, flags);
+	ret = packfile_store_for_each_object(files->packed, request, cb, cb_data, opts);
 	if (ret)
 		return ret;
 
@@ -122,6 +122,30 @@ static int odb_source_files_count_objects(struct odb_source *source,
 	return ret;
 }
 
+static int odb_source_files_find_abbrev_len(struct odb_source *source,
+					    const struct object_id *oid,
+					    unsigned min_len,
+					    unsigned *out)
+{
+	struct odb_source_files *files = odb_source_files_downcast(source);
+	unsigned len = min_len;
+	int ret;
+
+	ret = packfile_store_find_abbrev_len(files->packed, oid, len, &len);
+	if (ret < 0)
+		goto out;
+
+	ret = odb_source_loose_find_abbrev_len(source, oid, len, &len);
+	if (ret < 0)
+		goto out;
+
+	*out = len;
+	ret = 0;
+
+out:
+	return ret;
+}
+
 static int odb_source_files_freshen_object(struct odb_source *source,
 					   const struct object_id *oid)
 {
@@ -137,7 +161,7 @@ static int odb_source_files_write_object(struct odb_source *source,
 					 enum object_type type,
 					 struct object_id *oid,
 					 struct object_id *compat_oid,
-					 unsigned flags)
+					 enum odb_write_object_flags flags)
 {
 	return odb_source_loose_write_object(source, buf, len, type,
 					     oid, compat_oid, flags);
@@ -250,6 +274,7 @@ struct odb_source_files *odb_source_files_new(struct object_database *odb,
 	files->base.read_object_stream = odb_source_files_read_object_stream;
 	files->base.for_each_object = odb_source_files_for_each_object;
 	files->base.count_objects = odb_source_files_count_objects;
+	files->base.find_abbrev_len = odb_source_files_find_abbrev_len;
 	files->base.freshen_object = odb_source_files_freshen_object;
 	files->base.write_object = odb_source_files_write_object;
 	files->base.write_object_stream = odb_source_files_write_object_stream;
diff --git a/odb/source-inmemory.c b/odb/source-inmemory.c
new file mode 100644
index 0000000..e004566
--- /dev/null
+++ b/odb/source-inmemory.c
@@ -0,0 +1,382 @@
+#include "git-compat-util.h"
+#include "object-file.h"
+#include "odb.h"
+#include "odb/source-inmemory.h"
+#include "odb/streaming.h"
+#include "oidtree.h"
+#include "repository.h"
+
+struct inmemory_object {
+	enum object_type type;
+	const void *buf;
+	unsigned long size;
+};
+
+static const struct inmemory_object *find_cached_object(struct odb_source_inmemory *source,
+							const struct object_id *oid)
+{
+	static const struct inmemory_object empty_tree = {
+		.type = OBJ_TREE,
+		.buf = "",
+	};
+	const struct inmemory_object *object;
+
+	if (source->objects) {
+		object = oidtree_get(source->objects, oid);
+		if (object)
+			return object;
+	}
+
+	if (oid->algo && oideq(oid, hash_algos[oid->algo].empty_tree))
+		return &empty_tree;
+
+	return NULL;
+}
+
+static void populate_object_info(struct odb_source_inmemory *source,
+				 struct object_info *oi,
+				 const struct inmemory_object *object)
+{
+	if (!oi)
+		return;
+
+	if (oi->typep)
+		*(oi->typep) = object->type;
+	if (oi->sizep)
+		*(oi->sizep) = object->size;
+	if (oi->disk_sizep)
+		*(oi->disk_sizep) = 0;
+	if (oi->delta_base_oid)
+		oidclr(oi->delta_base_oid, source->base.odb->repo->hash_algo);
+	if (oi->contentp)
+		*oi->contentp = xmemdupz(object->buf, object->size);
+	if (oi->mtimep)
+		*oi->mtimep = 0;
+	oi->whence = OI_CACHED;
+}
+
+static int odb_source_inmemory_read_object_info(struct odb_source *source,
+						const struct object_id *oid,
+						struct object_info *oi,
+						enum object_info_flags flags UNUSED)
+{
+	struct odb_source_inmemory *inmemory = odb_source_inmemory_downcast(source);
+	const struct inmemory_object *object;
+
+	object = find_cached_object(inmemory, oid);
+	if (!object)
+		return -1;
+
+	populate_object_info(inmemory, oi, object);
+	return 0;
+}
+
+struct odb_read_stream_inmemory {
+	struct odb_read_stream base;
+	const unsigned char *buf;
+	size_t offset;
+};
+
+static ssize_t odb_read_stream_inmemory_read(struct odb_read_stream *stream,
+					     char *buf, size_t buf_len)
+{
+	struct odb_read_stream_inmemory *inmemory =
+		container_of(stream, struct odb_read_stream_inmemory, base);
+	size_t bytes = buf_len;
+
+	if (buf_len > inmemory->base.size - inmemory->offset)
+		bytes = inmemory->base.size - inmemory->offset;
+
+	memcpy(buf, inmemory->buf + inmemory->offset, bytes);
+	inmemory->offset += bytes;
+
+	return bytes;
+}
+
+static int odb_read_stream_inmemory_close(struct odb_read_stream *stream UNUSED)
+{
+	return 0;
+}
+
+static int odb_source_inmemory_read_object_stream(struct odb_read_stream **out,
+						  struct odb_source *source,
+						  const struct object_id *oid)
+{
+	struct odb_source_inmemory *inmemory = odb_source_inmemory_downcast(source);
+	struct odb_read_stream_inmemory *stream;
+	const struct inmemory_object *object;
+
+	object = find_cached_object(inmemory, oid);
+	if (!object)
+		return -1;
+
+	CALLOC_ARRAY(stream, 1);
+	stream->base.read = odb_read_stream_inmemory_read;
+	stream->base.close = odb_read_stream_inmemory_close;
+	stream->base.size = object->size;
+	stream->base.type = object->type;
+	stream->buf = object->buf;
+
+	*out = &stream->base;
+	return 0;
+}
+
+struct odb_source_inmemory_for_each_object_data {
+	struct odb_source_inmemory *inmemory;
+	const struct object_info *request;
+	odb_for_each_object_cb cb;
+	void *cb_data;
+};
+
+static int odb_source_inmemory_for_each_object_cb(const struct object_id *oid,
+						  void *node_data, void *cb_data)
+{
+	struct odb_source_inmemory_for_each_object_data *data = cb_data;
+	struct inmemory_object *object = node_data;
+
+	if (data->request) {
+		struct object_info oi = *data->request;
+		populate_object_info(data->inmemory, &oi, object);
+		return data->cb(oid, &oi, data->cb_data);
+	} else {
+		return data->cb(oid, NULL, data->cb_data);
+	}
+}
+
+static int odb_source_inmemory_for_each_object(struct odb_source *source,
+					       const struct object_info *request,
+					       odb_for_each_object_cb cb,
+					       void *cb_data,
+					       const struct odb_for_each_object_options *opts)
+{
+	struct odb_source_inmemory *inmemory = odb_source_inmemory_downcast(source);
+	struct odb_source_inmemory_for_each_object_data payload = {
+		.inmemory = inmemory,
+		.request = request,
+		.cb = cb,
+		.cb_data = cb_data,
+	};
+	struct object_id null_oid = { 0 };
+
+	if ((opts->flags & ODB_FOR_EACH_OBJECT_PROMISOR_ONLY) ||
+	    (opts->flags & ODB_FOR_EACH_OBJECT_LOCAL_ONLY && !source->local))
+		return 0;
+	if (!inmemory->objects)
+		return 0;
+
+	return oidtree_each(inmemory->objects,
+			    opts->prefix ? opts->prefix : &null_oid, opts->prefix_hex_len,
+			    odb_source_inmemory_for_each_object_cb, &payload);
+}
+
+struct find_abbrev_len_data {
+	const struct object_id *oid;
+	unsigned len;
+};
+
+static int find_abbrev_len_cb(const struct object_id *oid,
+			      struct object_info *oi UNUSED,
+			      void *cb_data)
+{
+	struct find_abbrev_len_data *data = cb_data;
+	unsigned len = oid_common_prefix_hexlen(oid, data->oid);
+	if (len != hash_algos[oid->algo].hexsz && len >= data->len)
+		data->len = len + 1;
+	return 0;
+}
+
+static int odb_source_inmemory_find_abbrev_len(struct odb_source *source,
+					       const struct object_id *oid,
+					       unsigned min_len,
+					       unsigned *out)
+{
+	struct odb_for_each_object_options opts = {
+		.prefix = oid,
+		.prefix_hex_len = min_len,
+	};
+	struct find_abbrev_len_data data = {
+		.oid = oid,
+		.len = min_len,
+	};
+	int ret;
+
+	ret = odb_source_inmemory_for_each_object(source, NULL, find_abbrev_len_cb,
+						  &data, &opts);
+	*out = data.len;
+
+	return ret;
+}
+
+static int count_objects_cb(const struct object_id *oid UNUSED,
+			    struct object_info *oi UNUSED,
+			    void *cb_data)
+{
+	unsigned long *counter = cb_data;
+	(*counter)++;
+	return 0;
+}
+
+static int odb_source_inmemory_count_objects(struct odb_source *source,
+					     enum odb_count_objects_flags flags UNUSED,
+					     unsigned long *out)
+{
+	struct odb_for_each_object_options opts = { 0 };
+	*out = 0;
+	return odb_source_inmemory_for_each_object(source, NULL, count_objects_cb,
+						   out, &opts);
+}
+
+static int odb_source_inmemory_write_object(struct odb_source *source,
+					    const void *buf, unsigned long len,
+					    enum object_type type,
+					    struct object_id *oid,
+					    struct object_id *compat_oid UNUSED,
+					    enum odb_write_object_flags flags UNUSED)
+{
+	struct odb_source_inmemory *inmemory = odb_source_inmemory_downcast(source);
+	struct inmemory_object *object;
+
+	hash_object_file(source->odb->repo->hash_algo, buf, len, type, oid);
+
+	if (!inmemory->objects) {
+		CALLOC_ARRAY(inmemory->objects, 1);
+		oidtree_init(inmemory->objects);
+	} else if (oidtree_contains(inmemory->objects, oid)) {
+		return 0;
+	}
+
+	CALLOC_ARRAY(object, 1);
+	object->size = len;
+	object->type = type;
+	object->buf = xmemdupz(buf, len);
+
+	oidtree_insert(inmemory->objects, oid, object);
+
+	return 0;
+}
+
+static int odb_source_inmemory_write_object_stream(struct odb_source *source,
+						   struct odb_write_stream *stream,
+						   size_t len,
+						   struct object_id *oid)
+{
+	char buf[16384];
+	size_t total_read = 0;
+	char *data;
+	int ret;
+
+	CALLOC_ARRAY(data, len);
+	while (!stream->is_finished) {
+		ssize_t bytes_read;
+
+		bytes_read = odb_write_stream_read(stream, buf, sizeof(buf));
+		if (total_read + bytes_read > len) {
+			ret = error("object stream yielded more bytes than expected");
+			goto out;
+		}
+
+		memcpy(data + total_read, buf, bytes_read);
+		total_read += bytes_read;
+	}
+
+	if (total_read != len) {
+		ret = error("object stream yielded less bytes than expected");
+		goto out;
+	}
+
+	ret = odb_source_inmemory_write_object(source, data, len, OBJ_BLOB, oid,
+					       NULL, 0);
+	if (ret < 0)
+		goto out;
+
+out:
+	free(data);
+	return ret;
+}
+
+static int odb_source_inmemory_freshen_object(struct odb_source *source,
+					      const struct object_id *oid)
+{
+	struct odb_source_inmemory *inmemory = odb_source_inmemory_downcast(source);
+	if (find_cached_object(inmemory, oid))
+		return 1;
+	return 0;
+}
+
+static int odb_source_inmemory_begin_transaction(struct odb_source *source UNUSED,
+						 struct odb_transaction **out UNUSED)
+{
+	return error("in-memory source does not support transactions");
+}
+
+static int odb_source_inmemory_read_alternates(struct odb_source *source UNUSED,
+					       struct strvec *out UNUSED)
+{
+	return 0;
+}
+
+static int odb_source_inmemory_write_alternate(struct odb_source *source UNUSED,
+					       const char *alternate UNUSED)
+{
+	return error("in-memory source does not support alternates");
+}
+
+static void odb_source_inmemory_close(struct odb_source *source UNUSED)
+{
+}
+
+static void odb_source_inmemory_reprepare(struct odb_source *source UNUSED)
+{
+}
+
+static int inmemory_object_free(const struct object_id *oid UNUSED,
+				void *node_data,
+				void *cb_data UNUSED)
+{
+	struct inmemory_object *object = node_data;
+	free((void *) object->buf);
+	free(object);
+	return 0;
+}
+
+static void odb_source_inmemory_free(struct odb_source *source)
+{
+	struct odb_source_inmemory *inmemory = odb_source_inmemory_downcast(source);
+
+	if (inmemory->objects) {
+		struct object_id null_oid = { 0 };
+
+		oidtree_each(inmemory->objects, &null_oid, 0,
+			     inmemory_object_free, NULL);
+		oidtree_clear(inmemory->objects);
+		free(inmemory->objects);
+	}
+
+	free(inmemory->base.path);
+	free(inmemory);
+}
+
+struct odb_source_inmemory *odb_source_inmemory_new(struct object_database *odb)
+{
+	struct odb_source_inmemory *source;
+
+	CALLOC_ARRAY(source, 1);
+	odb_source_init(&source->base, odb, ODB_SOURCE_INMEMORY, "source", false);
+
+	source->base.free = odb_source_inmemory_free;
+	source->base.close = odb_source_inmemory_close;
+	source->base.reprepare = odb_source_inmemory_reprepare;
+	source->base.read_object_info = odb_source_inmemory_read_object_info;
+	source->base.read_object_stream = odb_source_inmemory_read_object_stream;
+	source->base.for_each_object = odb_source_inmemory_for_each_object;
+	source->base.find_abbrev_len = odb_source_inmemory_find_abbrev_len;
+	source->base.count_objects = odb_source_inmemory_count_objects;
+	source->base.write_object = odb_source_inmemory_write_object;
+	source->base.write_object_stream = odb_source_inmemory_write_object_stream;
+	source->base.freshen_object = odb_source_inmemory_freshen_object;
+	source->base.begin_transaction = odb_source_inmemory_begin_transaction;
+	source->base.read_alternates = odb_source_inmemory_read_alternates;
+	source->base.write_alternate = odb_source_inmemory_write_alternate;
+
+	return source;
+}
diff --git a/odb/source-inmemory.h b/odb/source-inmemory.h
new file mode 100644
index 0000000..a88fc2e
--- /dev/null
+++ b/odb/source-inmemory.h
@@ -0,0 +1,33 @@
+#ifndef ODB_SOURCE_INMEMORY_H
+#define ODB_SOURCE_INMEMORY_H
+
+#include "odb/source.h"
+
+struct oidtree;
+
+/*
+ * An in-memory source that you can write objects to that shall be made
+ * available for reading, but that shouldn't ever be persisted to disk. Note
+ * that any objects written to this source will be stored in memory, so the
+ * number of objects you can store is limited by available system memory.
+ */
+struct odb_source_inmemory {
+	struct odb_source base;
+	struct oidtree *objects;
+};
+
+/* Create a new in-memory object database source. */
+struct odb_source_inmemory *odb_source_inmemory_new(struct object_database *odb);
+
+/*
+ * Cast the given object database source to the in-memory backend. This will
+ * cause a BUG in case the source doesn't use this backend.
+ */
+static inline struct odb_source_inmemory *odb_source_inmemory_downcast(struct odb_source *source)
+{
+	if (source->type != ODB_SOURCE_INMEMORY)
+		BUG("trying to downcast source of type '%d' to in-memory", source->type);
+	return container_of(source, struct odb_source_inmemory, base);
+}
+
+#endif
diff --git a/odb/source.h b/odb/source.h
index 96c906e..0a44088 100644
--- a/odb/source.h
+++ b/odb/source.h
@@ -13,6 +13,9 @@ enum odb_source_type {
 
 	/* The "files" backend that uses loose objects and packfiles. */
 	ODB_SOURCE_FILES,
+
+	/* The "in-memory" backend that stores objects in memory. */
+	ODB_SOURCE_INMEMORY,
 };
 
 struct object_id;
@@ -140,7 +143,7 @@ struct odb_source {
 			       const struct object_info *request,
 			       odb_for_each_object_cb cb,
 			       void *cb_data,
-			       unsigned flags);
+			       const struct odb_for_each_object_options *opts);
 
 	/*
 	 * This callback is expected to count objects in the given object
@@ -158,6 +161,18 @@ struct odb_source {
 			     unsigned long *out);
 
 	/*
+	 * This callback is expected to find the minimum required length to
+	 * make the given object ID unique.
+	 *
+	 * The callback is expected to return a negative error code in case it
+	 * failed, 0 otherwise.
+	 */
+	int (*find_abbrev_len)(struct odb_source *source,
+			       const struct object_id *oid,
+			       unsigned min_length,
+			       unsigned *out);
+
+	/*
 	 * This callback is expected to freshen the given object so that its
 	 * last access time is set to the current time. This is used to ensure
 	 * that objects that are recent will not get garbage collected even if
@@ -185,7 +200,7 @@ struct odb_source {
 			    enum object_type type,
 			    struct object_id *oid,
 			    struct object_id *compat_oid,
-			    unsigned flags);
+			    enum odb_write_object_flags flags);
 
 	/*
 	 * This callback is expected to persist the given object stream into
@@ -343,9 +358,9 @@ static inline int odb_source_for_each_object(struct odb_source *source,
 					     const struct object_info *request,
 					     odb_for_each_object_cb cb,
 					     void *cb_data,
-					     unsigned flags)
+					     const struct odb_for_each_object_options *opts)
 {
-	return source->for_each_object(source, request, cb, cb_data, flags);
+	return source->for_each_object(source, request, cb, cb_data, opts);
 }
 
 /*
@@ -361,6 +376,18 @@ static inline int odb_source_count_objects(struct odb_source *source,
 }
 
 /*
+ * Determine the minimum required length to make the given object ID unique in
+ * the given source. Returns 0 on success, a negative error code otherwise.
+ */
+static inline int odb_source_find_abbrev_len(struct odb_source *source,
+					     const struct object_id *oid,
+					     unsigned min_len,
+					     unsigned *out)
+{
+	return source->find_abbrev_len(source, oid, min_len, out);
+}
+
+/*
  * Freshen an object in the object database by updating its timestamp.
  * Returns 1 in case the object has been freshened, 0 in case the object does
  * not exist.
@@ -381,7 +408,7 @@ static inline int odb_source_write_object(struct odb_source *source,
 					  enum object_type type,
 					  struct object_id *oid,
 					  struct object_id *compat_oid,
-					  unsigned flags)
+					  enum odb_write_object_flags flags)
 {
 	return source->write_object(source, buf, len, type, oid,
 				    compat_oid, flags);
diff --git a/odb/streaming.c b/odb/streaming.c
index 5927a12..7602a8d 100644
--- a/odb/streaming.c
+++ b/odb/streaming.c
@@ -157,15 +157,26 @@ static int open_istream_incore(struct odb_read_stream **out,
 		.base.read = read_istream_incore,
 	};
 	struct odb_incore_read_stream *st;
+	unsigned long size_ul;
 	int ret;
 
 	oi.typep = &stream.base.type;
-	oi.sizep = &stream.base.size;
+	/*
+	 * object_info.sizep is unsigned long* (32-bit on Windows), but
+	 * stream.base.size is size_t (64-bit). We use a temporary variable
+	 * because the types are incompatible. Note: this path still truncates
+	 * for >4GB objects, but large objects should use pack streaming
+	 * (packfile_store_read_object_stream) which handles size_t properly.
+	 * This incore fallback is only used for small objects or when pack
+	 * streaming is unavailable.
+	 */
+	oi.sizep = &size_ul;
 	oi.contentp = (void **)&stream.buf;
 	ret = odb_read_object_info_extended(odb, oid, &oi,
 					    OBJECT_INFO_DIE_IF_CORRUPT);
 	if (ret)
 		return ret;
+	stream.base.size = size_ul;
 
 	CALLOC_ARRAY(st, 1);
 	*st = stream;
@@ -232,6 +243,16 @@ struct odb_read_stream *odb_read_stream_open(struct object_database *odb,
 	return st;
 }
 
+ssize_t odb_write_stream_read(struct odb_write_stream *st, void *buf, size_t sz)
+{
+	return st->read(st, buf, sz);
+}
+
+void odb_write_stream_release(struct odb_write_stream *st)
+{
+	free(st->data);
+}
+
 int odb_stream_blob_to_fd(struct object_database *odb,
 			  int fd,
 			  const struct object_id *oid,
@@ -287,3 +308,44 @@ int odb_stream_blob_to_fd(struct object_database *odb,
 	odb_read_stream_close(st);
 	return result;
 }
+
+struct read_object_fd_data {
+	int fd;
+	size_t remaining;
+};
+
+static ssize_t read_object_fd(struct odb_write_stream *stream,
+			      unsigned char *buf, size_t len)
+{
+	struct read_object_fd_data *data = stream->data;
+	ssize_t read_result;
+	size_t count;
+
+	if (stream->is_finished)
+		return 0;
+
+	count = data->remaining < len ? data->remaining : len;
+	read_result = read_in_full(data->fd, buf, count);
+	if (read_result < 0 || (size_t)read_result != count)
+		return -1;
+
+	data->remaining -= count;
+	if (!data->remaining)
+		stream->is_finished = 1;
+
+	return read_result;
+}
+
+void odb_write_stream_from_fd(struct odb_write_stream *stream, int fd,
+			      size_t size)
+{
+	struct read_object_fd_data *data;
+
+	CALLOC_ARRAY(data, 1);
+	data->fd = fd;
+	data->remaining = size;
+
+	stream->data = data;
+	stream->read = read_object_fd;
+	stream->is_finished = 0;
+}
diff --git a/odb/streaming.h b/odb/streaming.h
index c7861f7..c023671 100644
--- a/odb/streaming.h
+++ b/odb/streaming.h
@@ -5,6 +5,7 @@
 #define STREAMING_H 1
 
 #include "object.h"
+#include "odb.h"
 
 struct object_database;
 struct odb_read_stream;
@@ -21,7 +22,7 @@ struct odb_read_stream {
 	odb_read_stream_close_fn close;
 	odb_read_stream_read_fn read;
 	enum object_type type;
-	unsigned long size; /* inflated size of full object */
+	size_t size; /* inflated size of full object */
 };
 
 /*
@@ -48,6 +49,29 @@ int odb_read_stream_close(struct odb_read_stream *stream);
 ssize_t odb_read_stream_read(struct odb_read_stream *stream, void *buf, size_t len);
 
 /*
+ * A stream that provides an object to be written to the object database without
+ * loading all of it into memory.
+ */
+struct odb_write_stream {
+	ssize_t (*read)(struct odb_write_stream *, unsigned char *, size_t);
+	void *data;
+	int is_finished;
+};
+
+/*
+ * Read data from the stream into the buffer. Returns 0 when finished and the
+ * number of bytes read on success. Returns a negative error code in case
+ * reading from the stream fails.
+ */
+ssize_t odb_write_stream_read(struct odb_write_stream *stream, void *buf,
+			      size_t len);
+
+/*
+ * Releases memory allocated for underlying stream data.
+ */
+void odb_write_stream_release(struct odb_write_stream *stream);
+
+/*
  * Look up the object by its ID and write the full contents to the file
  * descriptor. The object must be a blob, or the function will fail. When
  * provided, the filter is used to transform the blob contents.
@@ -64,4 +88,10 @@ int odb_stream_blob_to_fd(struct object_database *odb,
 			  struct stream_filter *filter,
 			  int can_seek);
 
+/*
+ * Sets up an ODB write stream that reads from an fd.
+ */
+void odb_write_stream_from_fd(struct odb_write_stream *stream, int fd,
+			      size_t size);
+
 #endif /* STREAMING_H */
diff --git a/odb/transaction.c b/odb/transaction.c
new file mode 100644
index 0000000..b16e07a
--- /dev/null
+++ b/odb/transaction.c
@@ -0,0 +1,35 @@
+#include "git-compat-util.h"
+#include "odb/source.h"
+#include "odb/transaction.h"
+
+struct odb_transaction *odb_transaction_begin(struct object_database *odb)
+{
+	if (odb->transaction)
+		return NULL;
+
+	odb_source_begin_transaction(odb->sources, &odb->transaction);
+
+	return odb->transaction;
+}
+
+void odb_transaction_commit(struct odb_transaction *transaction)
+{
+	if (!transaction)
+		return;
+
+	/*
+	 * Ensure the transaction ending matches the pending transaction.
+	 */
+	ASSERT(transaction == transaction->source->odb->transaction);
+
+	transaction->commit(transaction);
+	transaction->source->odb->transaction = NULL;
+	free(transaction);
+}
+
+int odb_transaction_write_object_stream(struct odb_transaction *transaction,
+					struct odb_write_stream *stream,
+					size_t len, struct object_id *oid)
+{
+	return transaction->write_object_stream(transaction, stream, len, oid);
+}
diff --git a/odb/transaction.h b/odb/transaction.h
new file mode 100644
index 0000000..854fda0
--- /dev/null
+++ b/odb/transaction.h
@@ -0,0 +1,57 @@
+#ifndef ODB_TRANSACTION_H
+#define ODB_TRANSACTION_H
+
+#include "odb.h"
+#include "odb/source.h"
+
+/*
+ * A transaction may be started for an object database prior to writing new
+ * objects via odb_transaction_begin(). These objects are not committed until
+ * odb_transaction_commit() is invoked. Only a single transaction may be pending
+ * at a time.
+ *
+ * Each ODB source is expected to implement its own transaction handling.
+ */
+struct odb_transaction {
+	/* The ODB source the transaction is opened against. */
+	struct odb_source *source;
+
+	/* The ODB source specific callback invoked to commit a transaction. */
+	void (*commit)(struct odb_transaction *transaction);
+
+	/*
+	 * This callback is expected to write the given object stream into
+	 * the ODB transaction. Note that for now, only blobs support streaming.
+	 *
+	 * The resulting object ID shall be written into the out pointer. The
+	 * callback is expected to return 0 on success, a negative error code
+	 * otherwise.
+	 */
+	int (*write_object_stream)(struct odb_transaction *transaction,
+				   struct odb_write_stream *stream, size_t len,
+				   struct object_id *oid);
+};
+
+/*
+ * Starts an ODB transaction. Subsequent objects are written to the transaction
+ * and not committed until odb_transaction_commit() is invoked on the
+ * transaction. If the ODB already has a pending transaction, NULL is returned.
+ */
+struct odb_transaction *odb_transaction_begin(struct object_database *odb);
+
+/*
+ * Commits an ODB transaction making the written objects visible. If the
+ * specified transaction is NULL, the function is a no-op.
+ */
+void odb_transaction_commit(struct odb_transaction *transaction);
+
+/*
+ * Writes the object in the provided stream into the transaction. The resulting
+ * object ID is written into the out pointer. Returns 0 on success, a negative
+ * error code otherwise.
+ */
+int odb_transaction_write_object_stream(struct odb_transaction *transaction,
+					struct odb_write_stream *stream,
+					size_t len, struct object_id *oid);
+
+#endif
diff --git a/oidtree.c b/oidtree.c
index 324de94..e43f180 100644
--- a/oidtree.c
+++ b/oidtree.c
@@ -6,17 +6,15 @@
 #include "oidtree.h"
 #include "hash.h"
 
-struct oidtree_iter_data {
-	oidtree_iter fn;
-	void *arg;
-	size_t *last_nibble_at;
-	uint32_t algo;
-	uint8_t last_byte;
+struct oidtree_node {
+	struct cb_node base;
+	struct object_id key;
+	void *data;
 };
 
 void oidtree_init(struct oidtree *ot)
 {
-	cb_init(&ot->tree);
+	cb_init(&ot->tree, offsetof(struct oidtree_node, key));
 	mem_pool_init(&ot->mem_pool, 0);
 }
 
@@ -28,22 +26,22 @@ void oidtree_clear(struct oidtree *ot)
 	}
 }
 
-void oidtree_insert(struct oidtree *ot, const struct object_id *oid)
+struct oidtree_data {
+	struct object_id oid;
+};
+
+void oidtree_insert(struct oidtree *ot, const struct object_id *oid,
+		    void *data)
 {
-	struct cb_node *on;
-	struct object_id k;
+	struct oidtree_node *on;
+	struct cb_node *node;
 
 	if (!oid->algo)
 		BUG("oidtree_insert requires oid->algo");
 
-	on = mem_pool_alloc(&ot->mem_pool, sizeof(*on) + sizeof(*oid));
-
-	/*
-	 * Clear the padding and copy the result in separate steps to
-	 * respect the 4-byte alignment needed by struct object_id.
-	 */
-	oidcpy(&k, oid);
-	memcpy(on->k, &k, sizeof(k));
+	on = mem_pool_alloc(&ot->mem_pool, sizeof(*on));
+	oidcpy(&on->key, oid);
+	on->data = data;
 
 	/*
 	 * n.b. Current callers won't get us duplicates, here.  If a
@@ -51,14 +49,19 @@ void oidtree_insert(struct oidtree *ot, const struct object_id *oid)
 	 * that won't be freed until oidtree_clear.  Currently it's not
 	 * worth maintaining a free list
 	 */
-	cb_insert(&ot->tree, on, sizeof(*oid));
+	node = cb_insert(&ot->tree, &on->base, sizeof(*oid));
+	if (node) {
+		struct oidtree_node *preexisting = container_of(node, struct oidtree_node, base);
+		preexisting->data = data;
+	}
 }
 
-
-int oidtree_contains(struct oidtree *ot, const struct object_id *oid)
+static struct oidtree_node *oidtree_lookup(struct oidtree *ot,
+					   const struct object_id *oid)
 {
 	struct object_id k;
 	size_t klen = sizeof(k);
+	struct cb_node *node;
 
 	oidcpy(&k, oid);
 
@@ -69,41 +72,61 @@ int oidtree_contains(struct oidtree *ot, const struct object_id *oid)
 	klen += BUILD_ASSERT_OR_ZERO(offsetof(struct object_id, hash) <
 				offsetof(struct object_id, algo));
 
-	return cb_lookup(&ot->tree, (const uint8_t *)&k, klen) ? 1 : 0;
+	node = cb_lookup(&ot->tree, (const uint8_t *)&k, klen);
+	return node ? container_of(node, struct oidtree_node, base) : NULL;
 }
 
-static enum cb_next iter(struct cb_node *n, void *arg)
+bool oidtree_contains(struct oidtree *ot, const struct object_id *oid)
 {
-	struct oidtree_iter_data *x = arg;
-	struct object_id k;
-
-	/* Copy to provide 4-byte alignment needed by struct object_id. */
-	memcpy(&k, n->k, sizeof(k));
-
-	if (x->algo != GIT_HASH_UNKNOWN && x->algo != k.algo)
-		return CB_CONTINUE;
-
-	if (x->last_nibble_at) {
-		if ((k.hash[*x->last_nibble_at] ^ x->last_byte) & 0xf0)
-			return CB_CONTINUE;
-	}
-
-	return x->fn(&k, x->arg);
+	struct oidtree_node *node = oidtree_lookup(ot, oid);
+	return node ? 1 : 0;
 }
 
-void oidtree_each(struct oidtree *ot, const struct object_id *oid,
-			size_t oidhexsz, oidtree_iter fn, void *arg)
+void *oidtree_get(struct oidtree *ot, const struct object_id *oid)
 {
-	size_t klen = oidhexsz / 2;
-	struct oidtree_iter_data x = { 0 };
-	assert(oidhexsz <= GIT_MAX_HEXSZ);
+	struct oidtree_node *node = oidtree_lookup(ot, oid);
+	return node ? node->data : NULL;
+}
 
-	x.fn = fn;
-	x.arg = arg;
-	x.algo = oid->algo;
-	if (oidhexsz & 1) {
-		x.last_byte = oid->hash[klen];
-		x.last_nibble_at = &klen;
+struct oidtree_each_data {
+	oidtree_each_cb cb;
+	void *cb_data;
+	size_t *last_nibble_at;
+	uint32_t algo;
+	uint8_t last_byte;
+};
+
+static int iter(struct cb_node *n, void *cb_data)
+{
+	struct oidtree_node *node = container_of(n, struct oidtree_node, base);
+	struct oidtree_each_data *data = cb_data;
+
+	if (data->algo != GIT_HASH_UNKNOWN && data->algo != node->key.algo)
+		return 0;
+
+	if (data->last_nibble_at) {
+		if ((node->key.hash[*data->last_nibble_at] ^ data->last_byte) & 0xf0)
+			return 0;
 	}
-	cb_each(&ot->tree, (const uint8_t *)oid, klen, iter, &x);
+
+	return data->cb(&node->key, node->data, data->cb_data);
+}
+
+int oidtree_each(struct oidtree *ot, const struct object_id *prefix,
+		 size_t prefix_hex_len, oidtree_each_cb cb, void *cb_data)
+{
+	struct oidtree_each_data data = {
+		.cb = cb,
+		.cb_data = cb_data,
+		.algo = prefix->algo,
+	};
+	size_t klen = prefix_hex_len / 2;
+	assert(prefix_hex_len <= GIT_MAX_HEXSZ);
+
+	if (prefix_hex_len & 1) {
+		data.last_byte = prefix->hash[klen];
+		data.last_nibble_at = &klen;
+	}
+
+	return cb_each(&ot->tree, prefix->hash, klen, iter, &data);
 }
diff --git a/oidtree.h b/oidtree.h
index 77898f5..baa5a43 100644
--- a/oidtree.h
+++ b/oidtree.h
@@ -5,18 +5,60 @@
 #include "hash.h"
 #include "mem-pool.h"
 
+/*
+ * OID trees are an efficient storage for object IDs that use a critbit tree
+ * internally. Common prefixes are duplicated and object IDs are stored in a
+ * way that allow easy iteration over the objects in lexicographic order. As a
+ * consequence, operations that want to enumerate all object IDs that match a
+ * given prefix can be answered efficiently.
+ *
+ * Note that it is not (yet) possible to store data other than the object IDs
+ * themselves in this tree.
+ */
 struct oidtree {
 	struct cb_tree tree;
 	struct mem_pool mem_pool;
 };
 
-void oidtree_init(struct oidtree *);
-void oidtree_clear(struct oidtree *);
-void oidtree_insert(struct oidtree *, const struct object_id *);
-int oidtree_contains(struct oidtree *, const struct object_id *);
+/* Initialize the oidtree so that it is ready for use. */
+void oidtree_init(struct oidtree *ot);
 
-typedef enum cb_next (*oidtree_iter)(const struct object_id *, void *data);
-void oidtree_each(struct oidtree *, const struct object_id *,
-			size_t oidhexsz, oidtree_iter, void *data);
+/*
+ * Release all memory associated with the oidtree and reinitialize it for
+ * subsequent use.
+ */
+void oidtree_clear(struct oidtree *ot);
+
+/*
+ * Insert the object ID into the tree and store the given pointer alongside
+ * with it. The data pointer of any preexisting entry will be overwritten.
+ */
+void oidtree_insert(struct oidtree *ot, const struct object_id *oid,
+		    void *data);
+
+/* Check whether the tree contains the given object ID. */
+bool oidtree_contains(struct oidtree *ot, const struct object_id *oid);
+
+/* Get the payload stored with the given object ID. */
+void *oidtree_get(struct oidtree *ot, const struct object_id *oid);
+
+/*
+ * Callback function used for `oidtree_each()`. Returning a non-zero exit code
+ * will cause iteration to stop. The exit code will be propagated to the caller
+ * of `oidtree_each()`.
+ */
+typedef int (*oidtree_each_cb)(const struct object_id *oid,
+			       void *node_data,
+			       void *cb_data);
+
+/*
+ * Iterate through all object IDs in the tree whose prefix matches the given
+ * object ID prefix and invoke the callback function on each of them.
+ *
+ * Returns any non-zero exit code from the provided callback function.
+ */
+int oidtree_each(struct oidtree *ot,
+		 const struct object_id *prefix, size_t prefix_hex_len,
+		 oidtree_each_cb cb, void *cb_data);
 
 #endif /* OIDTREE_H */
diff --git a/oss-fuzz/fuzz-pack-headers.c b/oss-fuzz/fuzz-pack-headers.c
index 150c0f5..ef61ab5 100644
--- a/oss-fuzz/fuzz-pack-headers.c
+++ b/oss-fuzz/fuzz-pack-headers.c
@@ -6,7 +6,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size);
 int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
 {
 	enum object_type type;
-	unsigned long len;
+	size_t len;
 
 	unpack_object_header_buffer((const unsigned char *)data,
 				    (unsigned long)size, &type, &len);
diff --git a/pack-bitmap-write.c b/pack-bitmap-write.c
index 8338d72..1bcb3f9 100644
--- a/pack-bitmap-write.c
+++ b/pack-bitmap-write.c
@@ -32,6 +32,7 @@ struct bitmapped_commit {
 	struct commit *commit;
 	struct ewah_bitmap *bitmap;
 	struct ewah_bitmap *write_as;
+	struct ewah_bitmap *pseudo_merge_parents;
 	int flags;
 	int xor_offset;
 	uint32_t commit_pos;
@@ -89,6 +90,7 @@ void bitmap_writer_free(struct bitmap_writer *writer)
 	ewah_free(writer->tags);
 
 	kh_destroy_oid_map(writer->bitmaps);
+	free(writer->pos_cache);
 
 	kh_foreach_value(writer->pseudo_merge_commits, idx,
 			 free_pseudo_merge_commit_idx(idx));
@@ -101,6 +103,7 @@ void bitmap_writer_free(struct bitmap_writer *writer)
 		if (bc->write_as != bc->bitmap)
 			ewah_free(bc->write_as);
 		ewah_free(bc->bitmap);
+		ewah_free(bc->pseudo_merge_parents);
 	}
 	free(writer->selected);
 }
@@ -209,38 +212,116 @@ void bitmap_writer_push_commit(struct bitmap_writer *writer,
 	writer->selected[writer->selected_nr].write_as = NULL;
 	writer->selected[writer->selected_nr].flags = 0;
 	writer->selected[writer->selected_nr].pseudo_merge = pseudo_merge;
+	writer->selected[writer->selected_nr].pseudo_merge_parents = NULL;
 
 	writer->selected_nr++;
 }
 
+struct bitmap_pos_cache_entry {
+	struct object_id oid;
+	uint32_t pos;
+};
+
+#define BITMAP_POS_MIN_CACHE_SIZE (1U << 10)
+#define BITMAP_POS_MAX_CACHE_SIZE (1U << 21)
+#define BITMAP_POS_CACHE_VALID    (1U << 31)
+
+static void bitmap_writer_init_pos_cache(struct bitmap_writer *writer)
+{
+	if (writer->pos_cache)
+		return;
+
+	writer->pos_cache_nr = BITMAP_POS_MIN_CACHE_SIZE;
+
+	while (writer->pos_cache_nr < writer->to_pack->nr_objects &&
+	       writer->pos_cache_nr < BITMAP_POS_MAX_CACHE_SIZE)
+		writer->pos_cache_nr <<= 1;
+
+	CALLOC_ARRAY(writer->pos_cache, writer->pos_cache_nr);
+}
+
+static size_t bitmap_writer_pos_cache_slot(struct bitmap_writer *writer,
+					   const struct object_id *oid)
+{
+	return oidhash(oid) & (writer->pos_cache_nr - 1);
+}
+
+static bool bitmap_writer_pos_cache_valid(struct bitmap_writer *writer,
+					  size_t slot)
+{
+	return !!(writer->pos_cache[slot].pos & BITMAP_POS_CACHE_VALID);
+}
+
+static int find_cached_object_pos(struct bitmap_writer *writer,
+				  const struct object_id *oid, uint32_t *pos)
+{
+	size_t slot = bitmap_writer_pos_cache_slot(writer, oid);
+
+	if (bitmap_writer_pos_cache_valid(writer, slot) &&
+	    oideq(&writer->pos_cache[slot].oid, oid)) {
+		writer->pos_cache_hits++;
+		*pos = writer->pos_cache[slot].pos & ~BITMAP_POS_CACHE_VALID;
+		return 1;
+	}
+
+	writer->pos_cache_misses++;
+	return 0;
+}
+
+static uint32_t store_cached_object_pos(struct bitmap_writer *writer,
+					const struct object_id *oid,
+					uint32_t pos)
+{
+	size_t slot;
+
+	if (pos & BITMAP_POS_CACHE_VALID)
+		return pos; /* too large to cache */
+
+	slot = bitmap_writer_pos_cache_slot(writer, oid);
+
+	oidcpy(&writer->pos_cache[slot].oid, oid);
+	writer->pos_cache[slot].pos = pos | BITMAP_POS_CACHE_VALID;
+
+	return pos;
+}
+
 static uint32_t find_object_pos(struct bitmap_writer *writer,
 				const struct object_id *oid, int *found)
 {
 	struct object_entry *entry;
+	uint32_t pos;
+
+	bitmap_writer_init_pos_cache(writer);
+
+	if (find_cached_object_pos(writer, oid, &pos)) {
+		if (found)
+			*found = 1;
+		return pos;
+	}
 
 	entry = packlist_find(writer->to_pack, oid);
 	if (entry) {
 		uint32_t base_objects = 0;
+
 		if (writer->midx)
 			base_objects = writer->midx->num_objects +
 				writer->midx->num_objects_in_base;
-
-		if (found)
-			*found = 1;
-		return oe_in_pack_pos(writer->to_pack, entry) + base_objects;
+		pos = oe_in_pack_pos(writer->to_pack, entry) + base_objects;
 	} else if (writer->midx) {
-		uint32_t at, pos;
+		uint32_t at;
 
 		if (!bsearch_midx(oid, writer->midx, &at))
 			goto missing;
 		if (midx_to_pack_pos(writer->midx, at, &pos) < 0)
 			goto missing;
-
-		if (found)
-			*found = 1;
-		return pos;
+	} else {
+		goto missing;
 	}
 
+	if (found)
+		*found = 1;
+	return store_cached_object_pos(writer, oid, pos);
+
 missing:
 	if (found)
 		*found = 0;
@@ -249,11 +330,40 @@ static uint32_t find_object_pos(struct bitmap_writer *writer,
 	return 0;
 }
 
+static int bitmapped_commit_date_cmp(const void *_a, const void *_b)
+{
+	const struct bitmapped_commit *a = _a;
+	const struct bitmapped_commit *b = _b;
+
+	if (a->commit->date < b->commit->date)
+		return -1;
+	if (a->commit->date > b->commit->date)
+		return 1;
+	return 0;
+}
+
 static void compute_xor_offsets(struct bitmap_writer *writer)
 {
 	static const int MAX_XOR_OFFSET_SEARCH = 10;
 
 	int i, next = 0;
+	int nr = bitmap_writer_nr_selected_commits(writer);
+
+	if (nr > 1) {
+		QSORT(writer->selected, nr, bitmapped_commit_date_cmp);
+
+		for (i = 0; i < nr; i++) {
+			struct bitmapped_commit *stored = &writer->selected[i];
+			khiter_t hash_pos = kh_get_oid_map(writer->bitmaps,
+							   stored->commit->object.oid);
+
+			if (hash_pos == kh_end(writer->bitmaps))
+				BUG("selected commit missing from bitmap map: %s",
+				    oid_to_hex(&stored->commit->object.oid));
+
+			kh_value(writer->bitmaps, hash_pos) = stored;
+		}
+	}
 
 	while (next < writer->selected_nr) {
 		struct bitmapped_commit *stored = &writer->selected[next];
@@ -336,13 +446,17 @@ static void bitmap_builder_init(struct bitmap_builder *bb,
 	revs.topo_order = 1;
 	revs.first_parent_only = 1;
 
-	for (i = 0; i < writer->selected_nr; i++) {
+	for (i = 0; i < bitmap_writer_nr_selected_commits(writer); i++) {
 		struct bitmapped_commit *bc = &writer->selected[i];
 		struct bb_commit *ent = bb_data_at(&bb->data, bc->commit);
 
+		if (bc->pseudo_merge)
+			BUG("unexpected pseudo-merge at %"PRIuMAX,
+			    (uintmax_t)i);
+
 		ent->selected = 1;
 		ent->maximal = 1;
-		ent->pseudo_merge = bc->pseudo_merge;
+		ent->pseudo_merge = 0;
 		ent->idx = i;
 
 		ent->commit_mask = bitmap_new();
@@ -456,22 +570,13 @@ static void bitmap_builder_clear(struct bitmap_builder *bb)
 
 static int fill_bitmap_tree(struct bitmap_writer *writer,
 			    struct bitmap *bitmap,
-			    struct tree *tree)
+			    struct tree *tree,
+			    uint32_t pos)
 {
 	int found;
-	uint32_t pos;
 	struct tree_desc desc;
 	struct name_entry entry;
 
-	/*
-	 * If our bit is already set, then there is nothing to do. Both this
-	 * tree and all of its children will be set.
-	 */
-	pos = find_object_pos(writer, &tree->object.oid, &found);
-	if (!found)
-		return -1;
-	if (bitmap_get(bitmap, pos))
-		return 0;
 	bitmap_set(bitmap, pos);
 
 	if (repo_parse_tree(writer->repo, tree) < 0)
@@ -482,8 +587,21 @@ static int fill_bitmap_tree(struct bitmap_writer *writer,
 	while (tree_entry(&desc, &entry)) {
 		switch (object_type(entry.mode)) {
 		case OBJ_TREE:
+			pos = find_object_pos(writer, &entry.oid, &found);
+			if (!found)
+				return -1;
+			if (bitmap_get(bitmap, pos)) {
+				/*
+				 * If our bit is already set, then there
+				 * is nothing to do. Both this tree and
+				 * all of its children will be set.
+				 */
+				break;
+			}
+
 			if (fill_bitmap_tree(writer, bitmap,
-					     lookup_tree(writer->repo, &entry.oid)) < 0)
+					     lookup_tree(writer->repo,
+							 &entry.oid), pos) < 0)
 				return -1;
 			break;
 		case OBJ_BLOB:
@@ -504,6 +622,11 @@ static int fill_bitmap_tree(struct bitmap_writer *writer,
 
 static int reused_bitmaps_nr;
 static int reused_pseudo_merge_bitmaps_nr;
+static int pseudo_merge_bitmap_nr;
+static int pseudo_merge_bitmap_parents;
+
+static int fill_bitmap_commit_calls_nr;
+static int fill_bitmap_commit_found_ancestor_nr;
 
 static int fill_bitmap_commit(struct bitmap_writer *writer,
 			      struct bb_commit *ent,
@@ -514,7 +637,14 @@ static int fill_bitmap_commit(struct bitmap_writer *writer,
 			      const uint32_t *mapping)
 {
 	int found;
+	int from_pseudo_merge = commit->object.flags & BITMAP_PSEUDO_MERGE;
 	uint32_t pos;
+
+	if (ent->pseudo_merge)
+		BUG("unexpected pseudo-merge commit in fill_bitmap_commit()");
+
+	fill_bitmap_commit_calls_nr++;
+
 	if (!ent->bitmap)
 		ent->bitmap = bitmap_new();
 
@@ -528,10 +658,7 @@ static int fill_bitmap_commit(struct bitmap_writer *writer,
 			struct ewah_bitmap *old;
 			struct bitmap *remapped = bitmap_new();
 
-			if (commit->object.flags & BITMAP_PSEUDO_MERGE)
-				old = pseudo_merge_bitmap_for_commit(old_bitmap, c);
-			else
-				old = bitmap_for_commit(old_bitmap, c);
+			old = bitmap_for_commit(old_bitmap, c);
 			/*
 			 * If this commit has an old bitmap, then translate that
 			 * bitmap and add its bits to this one. No need to walk
@@ -540,26 +667,65 @@ static int fill_bitmap_commit(struct bitmap_writer *writer,
 			if (old && !rebuild_bitmap(mapping, old, remapped)) {
 				bitmap_or(ent->bitmap, remapped);
 				bitmap_free(remapped);
-				if (commit->object.flags & BITMAP_PSEUDO_MERGE)
-					reused_pseudo_merge_bitmaps_nr++;
-				else
-					reused_bitmaps_nr++;
+				reused_bitmaps_nr++;
 				continue;
 			}
 			bitmap_free(remapped);
 		}
 
 		/*
+		 * If we encounter an ancestor for which we have already
+		 * computed a bitmap during this build (i.e. a regular
+		 * selected commit processed earlier in topo order), we can
+		 * short-circuit the walk: its stored bitmap already covers
+		 * the commit itself, its tree, and all of its ancestors.
+		 */
+		if (c != commit) {
+			khiter_t hash_pos = kh_get_oid_map(writer->bitmaps,
+							   c->object.oid);
+			if (hash_pos != kh_end(writer->bitmaps)) {
+				struct bitmapped_commit *stored =
+					kh_value(writer->bitmaps, hash_pos);
+				if (stored && stored->bitmap) {
+					fill_bitmap_commit_found_ancestor_nr++;
+					bitmap_or_ewah(ent->bitmap,
+						       stored->bitmap);
+					continue;
+				}
+			}
+		}
+
+		/*
 		 * Mark ourselves and queue our tree. The commit
 		 * walk ensures we cover all parents.
 		 */
 		if (!(c->object.flags & BITMAP_PSEUDO_MERGE)) {
+			struct tree *tree;
+
+			if (from_pseudo_merge && !c->object.parsed) {
+				/*
+				 * Commits reachable from selected
+				 * non-pseudo-merges are already parsed
+				 * by the regular bitmap build.
+				 *
+				 * However, pseudo-merge fills can also
+				 * reach commits that were not covered
+				 * there, so parse any such leftovers
+				 * before reading their tree or parents.
+				 */
+				if (repo_parse_commit(writer->repo, c))
+					return -1;
+			}
+
 			pos = find_object_pos(writer, &c->object.oid, &found);
 			if (!found)
 				return -1;
 			bitmap_set(ent->bitmap, pos);
-			prio_queue_put(tree_queue,
-				       repo_get_commit_tree(writer->repo, c));
+
+			tree = repo_get_commit_tree(writer->repo, c);
+			if (!tree)
+				return -1;
+			prio_queue_put(tree_queue, tree);
 		}
 
 		for (p = c->parents; p; p = p->next) {
@@ -575,13 +741,158 @@ static int fill_bitmap_commit(struct bitmap_writer *writer,
 	}
 
 	while (tree_queue->nr) {
-		if (fill_bitmap_tree(writer, ent->bitmap,
-				     prio_queue_get(tree_queue)) < 0)
+		struct tree *t = prio_queue_get(tree_queue);
+		int found;
+
+		pos = find_object_pos(writer, &t->object.oid, &found);
+		if (!found)
+			return -1;
+		if (bitmap_get(ent->bitmap, pos)) {
+			/*
+			 * If our bit is already set, then there is
+			 * nothing to do. Both this tree and all of its
+			 * children will be set.
+			 */
+			continue;
+		}
+
+		if (fill_bitmap_tree(writer, ent->bitmap, t, pos) < 0)
 			return -1;
 	}
 	return 0;
 }
 
+static int reuse_pseudo_merge_bitmap(struct bitmap_index *old_bitmap,
+				     const uint32_t *mapping,
+				     struct commit *merge,
+				     struct ewah_bitmap **out)
+{
+	struct ewah_bitmap *old;
+	struct bitmap *remapped;
+
+	if (!old_bitmap || !mapping)
+		return 0;
+
+	old = pseudo_merge_bitmap_for_commit(old_bitmap, merge);
+	if (!old)
+		return 0;
+
+	remapped = bitmap_new();
+	if (rebuild_bitmap(mapping, old, remapped) < 0) {
+		bitmap_free(remapped);
+		return 0;
+	}
+
+	*out = bitmap_to_ewah(remapped);
+	bitmap_free(remapped);
+	reused_pseudo_merge_bitmaps_nr++;
+	return 1;
+}
+
+static int build_pseudo_merge_bitmap(struct bitmap_writer *writer,
+				     struct bitmap_index *old_bitmap,
+				     const uint32_t *mapping,
+				     struct commit *merge,
+				     struct ewah_bitmap **out)
+{
+	struct bb_commit ent = { 0 };
+	struct prio_queue queue = { NULL };
+	struct prio_queue tree_queue = { NULL };
+	unsigned parents = commit_list_count(merge->parents);
+	int ret;
+
+	ent.bitmap = bitmap_new();
+
+	pseudo_merge_bitmap_nr++;
+	pseudo_merge_bitmap_parents += parents;
+
+	if (reuse_pseudo_merge_bitmap(old_bitmap, mapping, merge, out)) {
+		ret = 0;
+		goto done;
+	}
+
+	ret = fill_bitmap_commit(writer, &ent, merge, &queue, &tree_queue,
+				 old_bitmap, mapping);
+
+	if (!ret)
+		*out = bitmap_to_ewah(ent.bitmap);
+
+done:
+	bitmap_free(ent.bitmap);
+	clear_prio_queue(&queue);
+	clear_prio_queue(&tree_queue);
+
+	return ret;
+}
+
+static int build_pseudo_merge_bitmaps(struct bitmap_writer *writer,
+				      struct bitmap_index *old_bitmap,
+				      const uint32_t *mapping,
+				      int *nr_stored)
+{
+	size_t i = bitmap_writer_nr_selected_commits(writer);
+	int ret = 0;
+
+	if (!writer->pseudo_merges_nr)
+		return 0;
+
+	trace2_region_enter("pack-bitmap-write", "building_pseudo_merge_bitmaps",
+			    writer->repo);
+
+	for (; i < writer->selected_nr; i++) {
+		struct bitmapped_commit *merge = &writer->selected[i];
+		struct commit_list *p;
+		struct bitmap *parents = bitmap_new();
+		struct ewah_bitmap *objects = NULL;
+
+		if (!merge->pseudo_merge)
+			BUG("found non-pseudo merge commit at %"PRIuMAX,
+			    (uintmax_t)i);
+
+		for (p = merge->commit->parents; p; p = p->next) {
+			int found;
+			uint32_t pos = find_object_pos(writer,
+						       &p->item->object.oid,
+						       &found);
+			if (!found) {
+				bitmap_free(parents);
+				ret = -1;
+				goto done;
+			}
+			bitmap_set(parents, pos);
+		}
+
+		merge->pseudo_merge_parents = bitmap_to_ewah(parents);
+		bitmap_free(parents);
+
+		if (build_pseudo_merge_bitmap(writer, old_bitmap, mapping,
+					      merge->commit, &objects) < 0) {
+			ret = -1;
+			goto done;
+		}
+		merge->bitmap = objects;
+
+		(*nr_stored)++;
+		display_progress(writer->progress, *nr_stored);
+	}
+
+done:
+	trace2_region_leave("pack-bitmap-write", "building_pseudo_merge_bitmaps",
+			    writer->repo);
+
+	trace2_data_intmax("pack-bitmap-write", writer->repo,
+			   "pseudo_merge_bitmap_nr",
+			   pseudo_merge_bitmap_nr);
+	trace2_data_intmax("pack-bitmap-write", writer->repo,
+			   "building_bitmaps_pseudo_merge_reused",
+			   reused_pseudo_merge_bitmaps_nr);
+	trace2_data_intmax("pack-bitmap-write", writer->repo,
+			   "pseudo_merge_bitmap_parents",
+			   pseudo_merge_bitmap_parents);
+
+	return ret;
+}
+
 static void store_selected(struct bitmap_writer *writer,
 			   struct bb_commit *ent, struct commit *commit)
 {
@@ -616,6 +927,10 @@ int bitmap_writer_build(struct bitmap_writer *writer)
 		writer->progress = start_progress(writer->repo,
 						  "Building bitmaps",
 						  writer->selected_nr);
+
+	writer->pos_cache_hits = 0;
+	writer->pos_cache_misses = 0;
+
 	trace2_region_enter("pack-bitmap-write", "building_bitmaps_total",
 			    writer->repo);
 
@@ -661,6 +976,10 @@ int bitmap_writer_build(struct bitmap_writer *writer)
 			bitmap_free(ent->bitmap);
 		ent->bitmap = NULL;
 	}
+	if (closed &&
+	    build_pseudo_merge_bitmaps(writer, old_bitmap, mapping,
+				       &nr_stored) < 0)
+		closed = 0;
 	clear_prio_queue(&queue);
 	clear_prio_queue(&tree_queue);
 	bitmap_builder_clear(&bb);
@@ -672,8 +991,15 @@ int bitmap_writer_build(struct bitmap_writer *writer)
 	trace2_data_intmax("pack-bitmap-write", writer->repo,
 			   "building_bitmaps_reused", reused_bitmaps_nr);
 	trace2_data_intmax("pack-bitmap-write", writer->repo,
-			   "building_bitmaps_pseudo_merge_reused",
-			   reused_pseudo_merge_bitmaps_nr);
+			   "fill_bitmap_commit_calls_nr",
+			   fill_bitmap_commit_calls_nr);
+	trace2_data_intmax("pack-bitmap-write", writer->repo,
+			   "fill_bitmap_commit_found_ancestor_nr",
+			   fill_bitmap_commit_found_ancestor_nr);
+	trace2_data_intmax("pack-bitmap-write", writer->repo,
+			   "bitmap_pos_cache_hits", writer->pos_cache_hits);
+	trace2_data_intmax("pack-bitmap-write", writer->repo,
+			   "bitmap_pos_cache_misses", writer->pos_cache_misses);
 
 	stop_progress(&writer->progress);
 
@@ -819,51 +1145,52 @@ static void write_selected_commits_v1(struct bitmap_writer *writer,
 	}
 }
 
+static int pseudo_merge_commit_pos_cmp(const void *_va, const void *_vb,
+				       void *_data)
+{
+	struct bitmap_writer *writer = _data;
+	uint32_t pos_a = find_object_pos(writer, _va, NULL);
+	uint32_t pos_b = find_object_pos(writer, _vb, NULL);
+
+	if (pos_a < pos_b)
+		return -1;
+	if (pos_a > pos_b)
+		return 1;
+	return 0;
+}
+
 static void write_pseudo_merges(struct bitmap_writer *writer,
 				struct hashfile *f)
 {
 	struct oid_array commits = OID_ARRAY_INIT;
-	struct bitmap **commits_bitmap = NULL;
 	off_t *pseudo_merge_ofs = NULL;
 	off_t start, table_start, next_ext;
 
 	uint32_t base = bitmap_writer_nr_selected_commits(writer);
 	size_t i, j = 0;
 
-	CALLOC_ARRAY(commits_bitmap, writer->pseudo_merges_nr);
 	CALLOC_ARRAY(pseudo_merge_ofs, writer->pseudo_merges_nr);
 
-	for (i = 0; i < writer->pseudo_merges_nr; i++) {
-		struct bitmapped_commit *merge = &writer->selected[base + i];
-		struct commit_list *p;
-
-		if (!merge->pseudo_merge)
-			BUG("found non-pseudo merge commit at %"PRIuMAX, (uintmax_t)i);
-
-		commits_bitmap[i] = bitmap_new();
-
-		for (p = merge->commit->parents; p; p = p->next)
-			bitmap_set(commits_bitmap[i],
-				   find_object_pos(writer, &p->item->object.oid,
-						   NULL));
-	}
-
 	start = hashfile_total(f);
 
 	for (i = 0; i < writer->pseudo_merges_nr; i++) {
-		struct ewah_bitmap *commits_ewah = bitmap_to_ewah(commits_bitmap[i]);
+		struct bitmapped_commit *merge = &writer->selected[base + i];
+
+		if (!merge->pseudo_merge)
+			BUG("found non-pseudo merge commit at %"PRIuMAX, (uintmax_t)i);
+
+		if (!merge->pseudo_merge_parents || !merge->bitmap)
+			BUG("missing pseudo-merge bitmap for commit %s",
+			    oid_to_hex(&merge->commit->object.oid));
 
 		pseudo_merge_ofs[i] = hashfile_total(f);
-
-		dump_bitmap(f, commits_ewah);
-		dump_bitmap(f, writer->selected[base+i].write_as);
-
-		ewah_free(commits_ewah);
+		dump_bitmap(f, merge->pseudo_merge_parents);
+		dump_bitmap(f, merge->bitmap);
 	}
 
 	next_ext = st_add(hashfile_total(f),
 			  st_mult(kh_size(writer->pseudo_merge_commits),
-				  sizeof(uint64_t)));
+				  sizeof(uint32_t) + sizeof(uint64_t)));
 
 	table_start = hashfile_total(f);
 
@@ -876,7 +1203,12 @@ static void write_pseudo_merges(struct bitmap_writer *writer,
 		oid_array_append(&commits, &kh_key(writer->pseudo_merge_commits, i));
 	}
 
-	oid_array_sort(&commits);
+	/*
+	 * Sort the commits by their bit position so that the lookup
+	 * table can be binary searched by the reader (see
+	 * find_pseudo_merge()).
+	 */
+	QSORT_S(commits.oid, commits.nr, pseudo_merge_commit_pos_cmp, writer);
 
 	/* write lookup table (non-extended) */
 	for (i = 0; i < commits.nr; i++) {
@@ -936,12 +1268,8 @@ static void write_pseudo_merges(struct bitmap_writer *writer,
 	hashwrite_be64(f, table_start - start);
 	hashwrite_be64(f, hashfile_total(f) - start + sizeof(uint64_t));
 
-	for (i = 0; i < writer->pseudo_merges_nr; i++)
-		bitmap_free(commits_bitmap[i]);
-
 	oid_array_clear(&commits);
 	free(pseudo_merge_ofs);
-	free(commits_bitmap);
 }
 
 static int table_cmp(const void *_va, const void *_vb, void *_data)
diff --git a/pack-bitmap.c b/pack-bitmap.c
index f6ec18d..f9af8a9 100644
--- a/pack-bitmap.c
+++ b/pack-bitmap.c
@@ -2270,7 +2270,7 @@ static int try_partial_reuse(struct bitmap_index *bitmap_git,
 {
 	off_t delta_obj_offset;
 	enum object_type type;
-	unsigned long size;
+	size_t size;
 
 	if (pack_pos >= pack->p->num_objects)
 		return -1; /* not actually in the pack */
diff --git a/pack-bitmap.h b/pack-bitmap.h
index a95e1c2..19a8655 100644
--- a/pack-bitmap.h
+++ b/pack-bitmap.h
@@ -132,6 +132,8 @@ int bitmap_has_oid_in_uninteresting(struct bitmap_index *, const struct object_i
 
 off_t get_disk_usage_from_bitmap(struct bitmap_index *, struct rev_info *);
 
+struct bitmap_pos_cache_entry;
+
 struct bitmap_writer {
 	struct repository *repo;
 	struct ewah_bitmap *commits;
@@ -143,6 +145,11 @@ struct bitmap_writer {
 	struct packing_data *to_pack;
 	struct multi_pack_index *midx; /* if appending to a MIDX chain */
 
+	struct bitmap_pos_cache_entry *pos_cache;
+	size_t pos_cache_nr;
+	uint64_t pos_cache_hits;
+	uint64_t pos_cache_misses;
+
 	struct bitmapped_commit *selected;
 	unsigned int selected_nr, selected_alloc;
 
diff --git a/pack-check.c b/pack-check.c
index 7378c80..2792f34 100644
--- a/pack-check.c
+++ b/pack-check.c
@@ -53,6 +53,7 @@ static int verify_packfile(struct repository *r,
 			   struct packed_git *p,
 			   struct pack_window **w_curs,
 			   verify_fn fn,
+			   void *fn_data,
 			   struct progress *progress, uint32_t base_count)
 
 {
@@ -109,7 +110,7 @@ static int verify_packfile(struct repository *r,
 		void *data;
 		struct object_id oid;
 		enum object_type type;
-		unsigned long size;
+		size_t size;
 		off_t curpos;
 		int data_valid;
 
@@ -142,7 +143,9 @@ static int verify_packfile(struct repository *r,
 			data = NULL;
 			data_valid = 0;
 		} else {
-			data = unpack_entry(r, p, entries[i].offset, &type, &size);
+			unsigned long sz;
+			data = unpack_entry(r, p, entries[i].offset, &type, &sz);
+			size = sz;
 			data_valid = 1;
 		}
 
@@ -161,7 +164,7 @@ static int verify_packfile(struct repository *r,
 				    oid_to_hex(&oid), p->pack_name);
 		else if (fn) {
 			int eaten = 0;
-			err |= fn(&oid, type, size, data, &eaten);
+			err |= fn(&oid, type, size, data, &eaten, fn_data);
 			if (eaten)
 				data = NULL;
 		}
@@ -192,7 +195,7 @@ int verify_pack_index(struct packed_git *p)
 	return err;
 }
 
-int verify_pack(struct repository *r, struct packed_git *p, verify_fn fn,
+int verify_pack(struct repository *r, struct packed_git *p, verify_fn fn, void *fn_data,
 		struct progress *progress, uint32_t base_count)
 {
 	int err = 0;
@@ -202,7 +205,7 @@ int verify_pack(struct repository *r, struct packed_git *p, verify_fn fn,
 	if (!p->index_data)
 		return -1;
 
-	err |= verify_packfile(r, p, &w_curs, fn, progress, base_count);
+	err |= verify_packfile(r, p, &w_curs, fn, fn_data, progress, base_count);
 	unuse_pack(&w_curs);
 
 	return err;
diff --git a/pack.h b/pack.h
index ec76472..1cde920 100644
--- a/pack.h
+++ b/pack.h
@@ -85,7 +85,11 @@ struct pack_idx_entry {
 
 struct progress;
 /* Note, the data argument could be NULL if object type is blob */
-typedef int (*verify_fn)(const struct object_id *, enum object_type, unsigned long, void*, int*);
+typedef int (*verify_fn)(const struct object_id *oid,
+			 enum object_type type,
+			 unsigned long size,
+			 void *buffer, int *eaten,
+			 void *fn_data);
 
 const char *write_idx_file(struct repository *repo,
 			   const char *index_name,
@@ -95,7 +99,8 @@ const char *write_idx_file(struct repository *repo,
 			   const unsigned char *sha1);
 int check_pack_crc(struct packed_git *p, struct pack_window **w_curs, off_t offset, off_t len, unsigned int nr);
 int verify_pack_index(struct packed_git *);
-int verify_pack(struct repository *, struct packed_git *, verify_fn fn, struct progress *, uint32_t);
+int verify_pack(struct repository *, struct packed_git *, verify_fn fn, void *fn_data,
+		struct progress *, uint32_t);
 off_t write_pack_header(struct hashfile *f, uint32_t);
 void fixup_pack_header_footer(const struct git_hash_algo *, int,
 			      unsigned char *, const char *, uint32_t,
diff --git a/packfile.c b/packfile.c
index d4de9f3..89366ab 100644
--- a/packfile.c
+++ b/packfile.c
@@ -420,6 +420,8 @@ void close_pack_index(struct packed_git *p)
 
 static void close_pack_revindex(struct packed_git *p)
 {
+	FREE_AND_NULL(p->revindex);
+
 	if (!p->revindex_map)
 		return;
 
@@ -1133,7 +1135,7 @@ int packfile_store_count_objects(struct packfile_store *store,
 }
 
 unsigned long unpack_object_header_buffer(const unsigned char *buf,
-		unsigned long len, enum object_type *type, unsigned long *sizep)
+		unsigned long len, enum object_type *type, size_t *sizep)
 {
 	unsigned shift;
 	size_t size, c;
@@ -1144,7 +1146,11 @@ unsigned long unpack_object_header_buffer(const unsigned char *buf,
 	size = c & 15;
 	shift = 4;
 	while (c & 0x80) {
-		if (len <= used || (bitsizeof(long) - 7) < shift) {
+		/*
+		 * Each continuation byte adds 7 bits. Ensure shift won't
+		 * overflow size_t (use size_t not long for 64-bit on Windows).
+		 */
+		if (len <= used || (bitsizeof(size_t) - 7) < shift) {
 			error("bad object header");
 			size = used = 0;
 			break;
@@ -1153,13 +1159,16 @@ unsigned long unpack_object_header_buffer(const unsigned char *buf,
 		size = st_add(size, st_left_shift(c & 0x7f, shift));
 		shift += 7;
 	}
-	*sizep = cast_size_t_to_ulong(size);
+	*sizep = size;
 	return used;
 }
 
-unsigned long get_size_from_delta(struct packed_git *p,
-				  struct pack_window **w_curs,
-				  off_t curpos)
+/*
+ * Size_t variant for >4GB delta results on Windows.
+ */
+static size_t get_size_from_delta_sz(struct packed_git *p,
+				     struct pack_window **w_curs,
+				     off_t curpos)
 {
 	const unsigned char *data;
 	unsigned char delta_head[20], *in;
@@ -1206,16 +1215,24 @@ unsigned long get_size_from_delta(struct packed_git *p,
 	data = delta_head;
 
 	/* ignore base size */
-	get_delta_hdr_size(&data, delta_head+sizeof(delta_head));
+	get_delta_hdr_size_sz(&data, delta_head+sizeof(delta_head));
 
 	/* Read the result size */
-	return get_delta_hdr_size(&data, delta_head+sizeof(delta_head));
+	return get_delta_hdr_size_sz(&data, delta_head+sizeof(delta_head));
+}
+
+unsigned long get_size_from_delta(struct packed_git *p,
+				  struct pack_window **w_curs,
+				  off_t curpos)
+{
+	size_t size = get_size_from_delta_sz(p, w_curs, curpos);
+	return cast_size_t_to_ulong(size);
 }
 
 int unpack_object_header(struct packed_git *p,
 			 struct pack_window **w_curs,
 			 off_t *curpos,
-			 unsigned long *sizep)
+			 size_t *sizep)
 {
 	unsigned char *base;
 	unsigned long left;
@@ -1367,7 +1384,7 @@ static enum object_type packed_to_object_type(struct repository *r,
 
 	while (type == OBJ_OFS_DELTA || type == OBJ_REF_DELTA) {
 		off_t base_offset;
-		unsigned long size;
+		size_t size;
 		/* Push the object we're going to leave behind */
 		if (poi_stack_nr >= poi_stack_alloc && poi_stack == small_poi_stack) {
 			poi_stack_alloc = alloc_nr(poi_stack_nr);
@@ -1586,7 +1603,7 @@ static int packed_object_info_with_index_pos(struct packed_git *p, off_t obj_off
 					     uint32_t *maybe_index_pos, struct object_info *oi)
 {
 	struct pack_window *w_curs = NULL;
-	unsigned long size;
+	size_t size;
 	off_t curpos = obj_offset;
 	enum object_type type = OBJ_NONE;
 	uint32_t pack_pos;
@@ -1614,14 +1631,18 @@ static int packed_object_info_with_index_pos(struct packed_git *p, off_t obj_off
 				ret = -1;
 				goto out;
 			}
-			*oi->sizep = get_size_from_delta(p, &w_curs, tmp_pos);
-			if (*oi->sizep == 0) {
+			/*
+			 * Use size_t variant to avoid die() on >4GB deltas.
+			 * oi->sizep is unsigned long, so truncation may occur,
+			 * but streaming code uses its own size_t tracking.
+			 */
+			size = get_size_from_delta_sz(p, &w_curs, tmp_pos);
+			if (size == 0) {
 				ret = -1;
 				goto out;
 			}
-		} else {
-			*oi->sizep = size;
 		}
+		*oi->sizep = (unsigned long)size;
 	}
 
 	if (oi->disk_sizep || (oi->mtimep && p->is_cruft)) {
@@ -1778,7 +1799,7 @@ void *unpack_entry(struct repository *r, struct packed_git *p, off_t obj_offset,
 	struct pack_window *w_curs = NULL;
 	off_t curpos = obj_offset;
 	void *data = NULL;
-	unsigned long size;
+	size_t size;
 	enum object_type type;
 	struct unpack_entry_stack_ent small_delta_stack[UNPACK_ENTRY_STACK_PREALLOC];
 	struct unpack_entry_stack_ent *delta_stack = small_delta_stack;
@@ -1943,8 +1964,10 @@ void *unpack_entry(struct repository *r, struct packed_git *p, off_t obj_offset,
 			      (uintmax_t)curpos, p->pack_name);
 			data = NULL;
 		} else {
+			unsigned long sz;
 			data = patch_delta(base, base_size, delta_data,
-					   delta_size, &size);
+					   delta_size, &sz);
+			size = sz;
 
 			/*
 			 * We could not apply the delta; warn the user, but
@@ -2244,7 +2267,8 @@ struct packed_git **packfile_store_get_kept_pack_cache(struct packfile_store *st
 			struct packed_git *p = e->pack;
 
 			if ((p->pack_keep && (flags & KEPT_PACK_ON_DISK)) ||
-			    (p->pack_keep_in_core && (flags & KEPT_PACK_IN_CORE))) {
+			    (p->pack_keep_in_core && (flags & KEPT_PACK_IN_CORE)) ||
+			    (p->pack_keep_in_core_open && (flags & KEPT_PACK_IN_CORE_OPEN))) {
 				ALLOC_GROW(packs, nr + 1, alloc);
 				packs[nr++] = p;
 			}
@@ -2299,7 +2323,7 @@ int has_object_kept_pack(struct repository *r, const struct object_id *oid,
 
 int for_each_object_in_pack(struct packed_git *p,
 			    each_packed_object_fn cb, void *data,
-			    unsigned flags)
+			    enum odb_for_each_object_flags flags)
 {
 	uint32_t i;
 	int r = 0;
@@ -2371,11 +2395,182 @@ static int packfile_store_for_each_object_wrapper(const struct object_id *oid,
 	}
 }
 
+static int match_hash(unsigned len, const unsigned char *a, const unsigned char *b)
+{
+	do {
+		if (*a != *b)
+			return 0;
+		a++;
+		b++;
+		len -= 2;
+	} while (len > 1);
+	if (len)
+		if ((*a ^ *b) & 0xf0)
+			return 0;
+	return 1;
+}
+
+static int for_each_prefixed_object_in_midx(
+	struct packfile_store *store,
+	struct multi_pack_index *m,
+	const struct odb_for_each_object_options *opts,
+	struct packfile_store_for_each_object_wrapper_data *data)
+{
+	int ret;
+
+	for (; m; m = m->base_midx) {
+		uint32_t num, i, first = 0;
+		int len = opts->prefix_hex_len > m->source->odb->repo->hash_algo->hexsz ?
+			m->source->odb->repo->hash_algo->hexsz : opts->prefix_hex_len;
+
+		if (!m->num_objects)
+			continue;
+
+		num = m->num_objects + m->num_objects_in_base;
+
+		bsearch_one_midx(opts->prefix, m, &first);
+
+		/*
+		 * At this point, "first" is the location of the lowest
+		 * object with an object name that could match "opts->prefix".
+		 * See if we have 0, 1 or more objects that actually match(es).
+		 */
+		for (i = first; i < num; i++) {
+			const struct object_id *current = NULL;
+			struct object_id oid;
+
+			current = nth_midxed_object_oid(&oid, m, i);
+
+			if (!match_hash(len, opts->prefix->hash, current->hash))
+				break;
+
+			if (data->request) {
+				struct object_info oi = *data->request;
+
+				ret = packfile_store_read_object_info(store, current,
+								      &oi, 0);
+				if (ret)
+					goto out;
+
+				ret = data->cb(&oid, &oi, data->cb_data);
+				if (ret)
+					goto out;
+			} else {
+				ret = data->cb(&oid, NULL, data->cb_data);
+				if (ret)
+					goto out;
+			}
+		}
+	}
+
+	ret = 0;
+
+out:
+	return ret;
+}
+
+static int for_each_prefixed_object_in_pack(
+	struct packfile_store *store,
+	struct packed_git *p,
+	const struct odb_for_each_object_options *opts,
+	struct packfile_store_for_each_object_wrapper_data *data)
+{
+	uint32_t num, i, first = 0;
+	int len = opts->prefix_hex_len > p->repo->hash_algo->hexsz ?
+		p->repo->hash_algo->hexsz : opts->prefix_hex_len;
+	int ret;
+
+	num = p->num_objects;
+	bsearch_pack(opts->prefix, p, &first);
+
+	/*
+	 * At this point, "first" is the location of the lowest object
+	 * with an object name that could match "bin_pfx".  See if we have
+	 * 0, 1 or more objects that actually match(es).
+	 */
+	for (i = first; i < num; i++) {
+		struct object_id oid;
+
+		nth_packed_object_id(&oid, p, i);
+		if (!match_hash(len, opts->prefix->hash, oid.hash))
+			break;
+
+		if (data->request) {
+			struct object_info oi = *data->request;
+
+			ret = packfile_store_read_object_info(store, &oid, &oi, 0);
+			if (ret)
+				goto out;
+
+			ret = data->cb(&oid, &oi, data->cb_data);
+			if (ret)
+				goto out;
+		} else {
+			ret = data->cb(&oid, NULL, data->cb_data);
+			if (ret)
+				goto out;
+		}
+	}
+
+	ret = 0;
+
+out:
+	return ret;
+}
+
+static int packfile_store_for_each_prefixed_object(
+	struct packfile_store *store,
+	const struct odb_for_each_object_options *opts,
+	struct packfile_store_for_each_object_wrapper_data *data)
+{
+	struct packfile_list_entry *e;
+	struct multi_pack_index *m;
+	bool pack_errors = false;
+	int ret;
+
+	if (opts->flags)
+		BUG("flags unsupported");
+
+	store->skip_mru_updates = true;
+
+	m = get_multi_pack_index(store->source);
+	if (m) {
+		ret = for_each_prefixed_object_in_midx(store, m, opts, data);
+		if (ret)
+			goto out;
+	}
+
+	for (e = packfile_store_get_packs(store); e; e = e->next) {
+		if (e->pack->multi_pack_index)
+			continue;
+
+		if (open_pack_index(e->pack)) {
+			pack_errors = true;
+			continue;
+		}
+
+		if (!e->pack->num_objects)
+			continue;
+
+		ret = for_each_prefixed_object_in_pack(store, e->pack, opts, data);
+		if (ret)
+			goto out;
+	}
+
+	ret = 0;
+
+out:
+	store->skip_mru_updates = false;
+	if (!ret && pack_errors)
+		ret = -1;
+	return ret;
+}
+
 int packfile_store_for_each_object(struct packfile_store *store,
 				   const struct object_info *request,
 				   odb_for_each_object_cb cb,
 				   void *cb_data,
-				   unsigned flags)
+				   const struct odb_for_each_object_options *opts)
 {
 	struct packfile_store_for_each_object_wrapper_data data = {
 		.store = store,
@@ -2386,20 +2581,23 @@ int packfile_store_for_each_object(struct packfile_store *store,
 	struct packfile_list_entry *e;
 	int pack_errors = 0, ret;
 
+	if (opts->prefix)
+		return packfile_store_for_each_prefixed_object(store, opts, &data);
+
 	store->skip_mru_updates = true;
 
 	for (e = packfile_store_get_packs(store); e; e = e->next) {
 		struct packed_git *p = e->pack;
 
-		if ((flags & ODB_FOR_EACH_OBJECT_LOCAL_ONLY) && !p->pack_local)
+		if ((opts->flags & ODB_FOR_EACH_OBJECT_LOCAL_ONLY) && !p->pack_local)
 			continue;
-		if ((flags & ODB_FOR_EACH_OBJECT_PROMISOR_ONLY) &&
+		if ((opts->flags & ODB_FOR_EACH_OBJECT_PROMISOR_ONLY) &&
 		    !p->pack_promisor)
 			continue;
-		if ((flags & ODB_FOR_EACH_OBJECT_SKIP_IN_CORE_KEPT_PACKS) &&
+		if ((opts->flags & ODB_FOR_EACH_OBJECT_SKIP_IN_CORE_KEPT_PACKS) &&
 		    p->pack_keep_in_core)
 			continue;
-		if ((flags & ODB_FOR_EACH_OBJECT_SKIP_ON_DISK_KEPT_PACKS) &&
+		if ((opts->flags & ODB_FOR_EACH_OBJECT_SKIP_ON_DISK_KEPT_PACKS) &&
 		    p->pack_keep)
 			continue;
 		if (open_pack_index(p)) {
@@ -2408,7 +2606,7 @@ int packfile_store_for_each_object(struct packfile_store *store,
 		}
 
 		ret = for_each_object_in_pack(p, packfile_store_for_each_object_wrapper,
-					      &data, flags);
+					      &data, opts->flags);
 		if (ret)
 			goto out;
 	}
@@ -2423,6 +2621,117 @@ int packfile_store_for_each_object(struct packfile_store *store,
 	return ret;
 }
 
+static int extend_abbrev_len(const struct object_id *a,
+			     const struct object_id *b,
+			     unsigned *out)
+{
+	unsigned len = oid_common_prefix_hexlen(a, b);
+	if (len != hash_algos[a->algo].hexsz && len >= *out)
+		*out = len + 1;
+	return 0;
+}
+
+static void find_abbrev_len_for_midx(struct multi_pack_index *m,
+				     const struct object_id *oid,
+				     unsigned min_len,
+				     unsigned *out)
+{
+	unsigned len = min_len;
+
+	for (; m; m = m->base_midx) {
+		int match = 0;
+		uint32_t num, first = 0;
+		struct object_id found_oid;
+
+		if (!m->num_objects)
+			continue;
+
+		num = m->num_objects + m->num_objects_in_base;
+		match = bsearch_one_midx(oid, m, &first);
+
+		/*
+		 * first is now the position in the packfile where we
+		 * would insert the object ID if it does not exist (or the
+		 * position of the object ID if it does exist). Hence, we
+		 * consider a maximum of two objects nearby for the
+		 * abbreviation length.
+		 */
+
+		if (!match) {
+			if (nth_midxed_object_oid(&found_oid, m, first))
+				extend_abbrev_len(&found_oid, oid, &len);
+		} else if (first < num - 1) {
+			if (nth_midxed_object_oid(&found_oid, m, first + 1))
+				extend_abbrev_len(&found_oid, oid, &len);
+		}
+		if (first > 0) {
+			if (nth_midxed_object_oid(&found_oid, m, first - 1))
+				extend_abbrev_len(&found_oid, oid, &len);
+		}
+	}
+
+	*out = len;
+}
+
+static void find_abbrev_len_for_pack(struct packed_git *p,
+				     const struct object_id *oid,
+				     unsigned min_len,
+				     unsigned *out)
+{
+	int match;
+	uint32_t num, first = 0;
+	struct object_id found_oid;
+	unsigned len = min_len;
+
+	num = p->num_objects;
+	match = bsearch_pack(oid, p, &first);
+
+	/*
+	 * first is now the position in the packfile where we would insert
+	 * the object ID if it does not exist (or the position of mad->hash if
+	 * it does exist). Hence, we consider a maximum of two objects
+	 * nearby for the abbreviation length.
+	 */
+	if (!match) {
+		if (!nth_packed_object_id(&found_oid, p, first))
+			extend_abbrev_len(&found_oid, oid, &len);
+	} else if (first < num - 1) {
+		if (!nth_packed_object_id(&found_oid, p, first + 1))
+			extend_abbrev_len(&found_oid, oid, &len);
+	}
+	if (first > 0) {
+		if (!nth_packed_object_id(&found_oid, p, first - 1))
+			extend_abbrev_len(&found_oid, oid, &len);
+	}
+
+	*out = len;
+}
+
+int packfile_store_find_abbrev_len(struct packfile_store *store,
+				   const struct object_id *oid,
+				   unsigned min_len,
+				   unsigned *out)
+{
+	struct packfile_list_entry *e;
+	struct multi_pack_index *m;
+
+	m = get_multi_pack_index(store->source);
+	if (m)
+		find_abbrev_len_for_midx(m, oid, min_len, &min_len);
+
+	for (e = packfile_store_get_packs(store); e; e = e->next) {
+		if (e->pack->multi_pack_index)
+			continue;
+		if (open_pack_index(e->pack) || !e->pack->num_objects)
+			continue;
+
+		find_abbrev_len_for_pack(e->pack, oid, min_len, &min_len);
+	}
+
+	*out = min_len;
+	return 0;
+}
+
 struct add_promisor_object_data {
 	struct repository *repo;
 	struct oidset *set;
@@ -2643,7 +2952,7 @@ int packfile_read_object_stream(struct odb_read_stream **out,
 	struct odb_packed_read_stream *stream;
 	struct pack_window *window = NULL;
 	enum object_type in_pack_type;
-	unsigned long size;
+	size_t size;
 
 	in_pack_type = unpack_object_header(pack, &window, &offset, &size);
 	unuse_pack(&window);
diff --git a/packfile.h b/packfile.h
index a16ec39..49d6bde 100644
--- a/packfile.h
+++ b/packfile.h
@@ -28,6 +28,7 @@ struct packed_git {
 	unsigned pack_local:1,
 		 pack_keep:1,
 		 pack_keep_in_core:1,
+		 pack_keep_in_core_open:1,
 		 freshened:1,
 		 do_not_close:1,
 		 pack_promisor:1,
@@ -266,6 +267,7 @@ int packfile_store_freshen_object(struct packfile_store *store,
 enum kept_pack_type {
 	KEPT_PACK_ON_DISK = (1 << 0),
 	KEPT_PACK_IN_CORE = (1 << 1),
+	KEPT_PACK_IN_CORE_OPEN = (1 << 2),
 };
 
 /*
@@ -352,7 +354,7 @@ typedef int each_packed_object_fn(const struct object_id *oid,
 				  void *data);
 int for_each_object_in_pack(struct packed_git *p,
 			    each_packed_object_fn, void *data,
-			    unsigned flags);
+			    enum odb_for_each_object_flags flags);
 
 /*
  * Iterate through all packed objects in the given packfile store and invoke
@@ -367,7 +369,12 @@ int packfile_store_for_each_object(struct packfile_store *store,
 				   const struct object_info *request,
 				   odb_for_each_object_cb cb,
 				   void *cb_data,
-				   unsigned flags);
+				   const struct odb_for_each_object_options *opts);
+
+int packfile_store_find_abbrev_len(struct packfile_store *store,
+				   const struct object_id *oid,
+				   unsigned min_len,
+				   unsigned *out);
 
 /* A hook to report invalid files in pack directory */
 #define PACKDIR_FILE_PACK 1
@@ -449,9 +456,9 @@ off_t find_pack_entry_one(const struct object_id *oid, struct packed_git *);
 
 int is_pack_valid(struct packed_git *);
 void *unpack_entry(struct repository *r, struct packed_git *, off_t, enum object_type *, unsigned long *);
-unsigned long unpack_object_header_buffer(const unsigned char *buf, unsigned long len, enum object_type *type, unsigned long *sizep);
+unsigned long unpack_object_header_buffer(const unsigned char *buf, unsigned long len, enum object_type *type, size_t *sizep);
 unsigned long get_size_from_delta(struct packed_git *, struct pack_window **, off_t);
-int unpack_object_header(struct packed_git *, struct pack_window **, off_t *, unsigned long *);
+int unpack_object_header(struct packed_git *, struct pack_window **, off_t *, size_t *);
 off_t get_delta_base(struct packed_git *p, struct pack_window **w_curs,
 		     off_t *curpos, enum object_type type,
 		     off_t delta_obj_offset);
diff --git a/pager.c b/pager.c
index 5531fff..35b210e 100644
--- a/pager.c
+++ b/pager.c
@@ -108,10 +108,11 @@ const char *git_pager(struct repository *r, int stdout_is_tty)
 
 static void setup_pager_env(struct strvec *env)
 {
-	const char **argv;
+	char **argv;
 	int i;
 	char *pager_env = xstrdup(PAGER_ENV);
-	int n = split_cmdline(pager_env, &argv);
+	/* split_cmdline splits in place, so we know the result is writable */
+	int n = split_cmdline(pager_env, (const char ***)&argv);
 
 	if (n < 0)
 		die("malformed build-time PAGER_ENV: %s",
diff --git a/parse-options.h b/parse-options.h
index 706de97..0d1f738 100644
--- a/parse-options.h
+++ b/parse-options.h
@@ -117,6 +117,7 @@ typedef int parse_opt_subcommand_fn(int argc, const char **argv,
  *   PARSE_OPT_OPTARG: says that the argument is optional (not for BOOLEANs)
  *   PARSE_OPT_NOARG: says that this option does not take an argument
  *   PARSE_OPT_NONEG: says that this option cannot be negated
+ *                   (i.e. rejects "--no-<option>")
  *   PARSE_OPT_HIDDEN: this option is skipped in the default usage, and
  *                     shown only in the full usage.
  *   PARSE_OPT_LASTARG_DEFAULT: says that this option will take the default
diff --git a/parse.c b/parse.c
index 4831357..d77f280 100644
--- a/parse.c
+++ b/parse.c
@@ -107,6 +107,15 @@ int git_parse_int64(const char *value, int64_t *ret)
 	return 1;
 }
 
+int git_parse_uint(const char *value, unsigned int *ret)
+{
+	uintmax_t tmp;
+	if (!git_parse_unsigned(value, &tmp, maximum_unsigned_value_of_type(unsigned int)))
+		return 0;
+	*ret = tmp;
+	return 1;
+}
+
 int git_parse_ulong(const char *value, unsigned long *ret)
 {
 	uintmax_t tmp;
diff --git a/parse.h b/parse.h
index ea32de9..a6dd37c 100644
--- a/parse.h
+++ b/parse.h
@@ -5,6 +5,7 @@ int git_parse_signed(const char *value, intmax_t *ret, intmax_t max);
 int git_parse_unsigned(const char *value, uintmax_t *ret, uintmax_t max);
 int git_parse_ssize_t(const char *, ssize_t *);
 int git_parse_ulong(const char *, unsigned long *);
+int git_parse_uint(const char *value, unsigned int *ret);
 int git_parse_int(const char *value, int *ret);
 int git_parse_int64(const char *value, int64_t *ret);
 int git_parse_double(const char *value, double *ret);
diff --git a/patch-ids.h b/patch-ids.h
index 490d739..57534ee72 100644
--- a/patch-ids.h
+++ b/patch-ids.h
@@ -37,7 +37,7 @@ int has_commit_patch_id(struct commit *commit, struct patch_ids *);
  *   struct patch_id *cur;
  *   for (cur = patch_id_iter_first(commit, ids);
  *        cur;
- *        cur = patch_id_iter_next(cur, ids) {
+ *        cur = patch_id_iter_next(cur, ids)) {
  *           ... look at cur->commit
  *   }
  */
diff --git a/path-walk.c b/path-walk.c
index 2aa3e7d..94ff90b 100644
--- a/path-walk.c
+++ b/path-walk.c
@@ -9,6 +9,9 @@
 #include "hashmap.h"
 #include "hex.h"
 #include "list-objects.h"
+#include "list-objects-filter-options.h"
+#include "object-name.h"
+#include "odb.h"
 #include "object.h"
 #include "oid-array.h"
 #include "path.h"
@@ -174,15 +177,10 @@ static int add_tree_entries(struct path_walk_context *ctx,
 
 		if (!o) {
 			error(_("failed to find object %s"),
-			      oid_to_hex(&o->oid));
+			      oid_to_hex(&entry.oid));
 			return -1;
 		}
 
-		/* Skip this object if already seen. */
-		if (o->flags & SEEN)
-			continue;
-		o->flags |= SEEN;
-
 		strbuf_setlen(&path, base_len);
 		strbuf_add(&path, entry.path, entry.pathlen);
 
@@ -193,6 +191,40 @@ static int add_tree_entries(struct path_walk_context *ctx,
 		if (type == OBJ_TREE)
 			strbuf_addch(&path, '/');
 
+		if (o->flags & SEEN) {
+			/*
+			 * A tree with a shared OID may appear at multiple
+			 * paths. Even though we already added this tree to
+			 * the output at some other path, we still need to
+			 * walk into it at this in-cone path to discover
+			 * blobs that were not found at the earlier
+			 * out-of-cone path.
+			 *
+			 * Only do this for paths not yet in our map, to
+			 * avoid duplicate entries when the same tree OID
+			 * appears at the same path across multiple commits.
+			 */
+			if (type == OBJ_TREE && ctx->info->pl &&
+			    ctx->info->pl->use_cone_patterns &&
+			    !ctx->info->pl_sparse_trees &&
+			    !strmap_contains(&ctx->paths_to_lists, path.buf)) {
+				int dtype;
+				enum pattern_match_result m;
+				m = path_matches_pattern_list(path.buf, path.len,
+							      path.buf + base_len,
+							      &dtype,
+							      ctx->info->pl,
+							      ctx->repo->index);
+				if (m != NOT_MATCHED) {
+					add_path_to_list(ctx, path.buf, type,
+							 &entry.oid,
+							 !(o->flags & UNINTERESTING));
+					push_to_stack(ctx, path.buf);
+				}
+			}
+			continue;
+		}
+
 		if (ctx->info->pl) {
 			int dtype;
 			enum pattern_match_result match;
@@ -202,7 +234,8 @@ static int add_tree_entries(struct path_walk_context *ctx,
 							  ctx->repo->index);
 
 			if (ctx->info->pl->use_cone_patterns &&
-			    match == NOT_MATCHED)
+			    match == NOT_MATCHED &&
+			    (type == OBJ_BLOB || ctx->info->pl_sparse_trees))
 				continue;
 			else if (!ctx->info->pl->use_cone_patterns &&
 				 type == OBJ_BLOB &&
@@ -237,6 +270,7 @@ static int add_tree_entries(struct path_walk_context *ctx,
 				continue;
 		}
 
+		o->flags |= SEEN;
 		add_path_to_list(ctx, path.buf, type, &entry.oid,
 				 !(o->flags & UNINTERESTING));
 
@@ -249,6 +283,17 @@ static int add_tree_entries(struct path_walk_context *ctx,
 }
 
 /*
+ * Paths starting with '/' (e.g., "/tags", "/tagged-blobs") hold objects that
+ * were directly requested by 'pending' objects rather than discovered during
+ * tree traversal.
+ */
+static int path_is_for_direct_objects(const char *path)
+{
+	ASSERT(path);
+	return path[0] == '/';
+}
+
+/*
  * For each path in paths_to_explore, walk the trees another level
  * and add any found blobs to the batch (but only if they exist and
  * haven't been added yet).
@@ -306,23 +351,57 @@ static int walk_path(struct path_walk_context *ctx,
 
 	if (list->type == OBJ_BLOB &&
 	    ctx->revs->prune_data.nr &&
+	    !path_is_for_direct_objects(path) &&
 	    !match_pathspec(ctx->repo->index, &ctx->revs->prune_data,
 			   path, strlen(path), 0,
 			   NULL, 0))
 		return 0;
 
-	/* Evaluate function pointer on this data, if requested. */
-	if ((list->type == OBJ_TREE && ctx->info->trees) ||
-	    (list->type == OBJ_BLOB && ctx->info->blobs) ||
-	    (list->type == OBJ_TAG && ctx->info->tags))
+	/*
+	 * Evaluate function pointer on this data, if requested.
+	 * Ignore object type filters for tagged objects (path starts
+	 * with `/`), first for blobs and then other types.
+	 */
+	if (list->type == OBJ_BLOB &&
+	    ctx->info->blob_limit &&
+	    !path_is_for_direct_objects(path)) {
+		struct oid_array filtered = OID_ARRAY_INIT;
+
+		for (size_t i = 0; i < list->oids.nr; i++) {
+			unsigned long size;
+
+			if (odb_read_object_info(ctx->repo->objects,
+						 &list->oids.oid[i],
+						 &size) != OBJ_BLOB ||
+				size < ctx->info->blob_limit)
+				oid_array_append(&filtered,
+						 &list->oids.oid[i]);
+		}
+
+		if (filtered.nr)
+			ret = ctx->info->path_fn(path, &filtered, list->type,
+						 ctx->info->path_fn_data);
+		oid_array_clear(&filtered);
+	} else if ((!ctx->info->strict_types && path_is_for_direct_objects(path)) ||
+		   (list->type == OBJ_TREE && ctx->info->trees) ||
+		   (list->type == OBJ_BLOB && ctx->info->blobs) ||
+		   (list->type == OBJ_TAG && ctx->info->tags)) {
 		ret = ctx->info->path_fn(path, &list->oids, list->type,
 					ctx->info->path_fn_data);
+	}
 
-	/* Expand data for children. */
-	if (list->type == OBJ_TREE) {
+	/*
+	 * Expand tree children, except when the set is directly requested
+	 * _and_ we are otherwise filtering out trees.
+	 */
+	if (list->type == OBJ_TREE &&
+	    (!path_is_for_direct_objects(path) || ctx->info->trees)) {
+		/* Use root path if expanding from tagged/direct trees. */
+		const char *expand_path = !strcmp(path, "/tagged-trees")
+					  ? root_path : path;
 		for (size_t i = 0; i < list->oids.nr; i++) {
 			ret |= add_tree_entries(ctx,
-					    path,
+					    expand_path,
 					    &list->oids.oid[i]);
 		}
 	}
@@ -370,14 +449,12 @@ static int setup_pending_objects(struct path_walk_info *info,
 {
 	struct type_and_oid_list *tags = NULL;
 	struct type_and_oid_list *tagged_blobs = NULL;
-	struct type_and_oid_list *root_tree_list = NULL;
+	struct type_and_oid_list *tagged_trees = NULL;
 
 	if (info->tags)
 		CALLOC_ARRAY(tags, 1);
-	if (info->blobs)
-		CALLOC_ARRAY(tagged_blobs, 1);
-	if (info->trees)
-		root_tree_list = strmap_get(&ctx->paths_to_lists, root_path);
+	CALLOC_ARRAY(tagged_blobs, 1);
+	CALLOC_ARRAY(tagged_trees, 1);
 
 	/*
 	 * Pending objects include:
@@ -421,22 +498,19 @@ static int setup_pending_objects(struct path_walk_info *info,
 
 		switch (obj->type) {
 		case OBJ_TREE:
-			if (!info->trees)
-				continue;
-			if (pending->path) {
-				char *path = *pending->path ? xstrfmt("%s/", pending->path)
-							    : xstrdup("");
+			if (pending->path && *pending->path) {
+				char *path = xstrfmt("%s/", pending->path);
 				add_path_to_list(ctx, path, OBJ_TREE, &obj->oid, 1);
 				free(path);
+			} else if (!pending->path || !info->trees) {
+				oid_array_append(&tagged_trees->oids, &obj->oid);
 			} else {
-				/* assume a root tree, such as a lightweight tag. */
-				oid_array_append(&root_tree_list->oids, &obj->oid);
+				add_path_to_list(ctx, root_path, OBJ_TREE,
+						 &obj->oid, 1);
 			}
 			break;
 
 		case OBJ_BLOB:
-			if (!info->blobs)
-				continue;
 			if (pending->path)
 				add_path_to_list(ctx, pending->path, OBJ_BLOB, &obj->oid, 1);
 			else
@@ -469,6 +543,18 @@ static int setup_pending_objects(struct path_walk_info *info,
 			free(tagged_blobs);
 		}
 	}
+	if (tagged_trees) {
+		if (tagged_trees->oids.nr) {
+			const char *tagged_tree_path = "/tagged-trees";
+			tagged_trees->type = OBJ_TREE;
+			tagged_trees->maybe_interesting = 1;
+			strmap_put(&ctx->paths_to_lists, tagged_tree_path, tagged_trees);
+			push_to_stack(ctx, tagged_tree_path);
+		} else {
+			oid_array_clear(&tagged_trees->oids);
+			free(tagged_trees);
+		}
+	}
 	if (tags) {
 		if (tags->oids.nr) {
 			const char *tag_path = "/tags";
@@ -485,6 +571,123 @@ static int setup_pending_objects(struct path_walk_info *info,
 	return 0;
 }
 
+static int prepare_filters_one(struct path_walk_info *info,
+			       struct list_objects_filter_options *options)
+{
+	switch (options->choice) {
+	case LOFC_DISABLED:
+		return 1;
+
+	case LOFC_BLOB_NONE:
+		if (info) {
+			info->blobs = 0;
+			list_objects_filter_release(options);
+		}
+		return 1;
+
+	case LOFC_BLOB_LIMIT:
+		if (info) {
+			if (!options->blob_limit_value)
+				info->blobs = 0;
+			else if (!info->blob_limit ||
+				 info->blob_limit > options->blob_limit_value)
+				info->blob_limit = options->blob_limit_value;
+			list_objects_filter_release(options);
+		}
+		return 1;
+
+	case LOFC_TREE_DEPTH:
+		if (options->tree_exclude_depth) {
+			error(_("tree:%lu filter not supported by the path-walk API"),
+			      options->tree_exclude_depth);
+			return 0;
+		}
+		if (info) {
+			info->trees = 0;
+			info->blobs = 0;
+		}
+		return 1;
+
+	case LOFC_OBJECT_TYPE:
+		if (info) {
+			info->commits &= options->object_type == OBJ_COMMIT;
+			info->tags &= options->object_type == OBJ_TAG;
+			info->trees &= options->object_type == OBJ_TREE;
+			info->blobs &= options->object_type == OBJ_BLOB;
+			info->strict_types = 1;
+			list_objects_filter_release(options);
+		}
+		return 1;
+
+	case LOFC_SPARSE_OID:
+		if (info) {
+			struct object_id sparse_oid;
+			struct repository *repo = info->revs->repo;
+
+			if (info->pl) {
+				warning(_("sparse filter cannot be combined with existing sparse patterns"));
+				return 0;
+			}
+
+			if (repo_get_oid_with_flags(repo,
+						    options->sparse_oid_name,
+						    &sparse_oid,
+						    GET_OID_BLOB)) {
+				error(_("unable to access sparse blob in '%s'"),
+				      options->sparse_oid_name);
+				return 0;
+			}
+
+			CALLOC_ARRAY(info->pl, 1);
+			info->pl->use_cone_patterns = 1;
+
+			if (add_patterns_from_blob_to_list(&sparse_oid, "", 0,
+							   info->pl) < 0) {
+				clear_pattern_list(info->pl);
+				FREE_AND_NULL(info->pl);
+				error(_("unable to parse sparse filter data in '%s'"),
+				      oid_to_hex(&sparse_oid));
+				return 0;
+			}
+
+			if (!info->pl->use_cone_patterns) {
+				clear_pattern_list(info->pl);
+				FREE_AND_NULL(info->pl);
+				warning(_("sparse filter is not cone-mode compatible"));
+				return 0;
+			}
+		}
+		return 1;
+
+	case LOFC_COMBINE:
+		for (size_t i = 0; i < options->sub_nr; i++) {
+			if (!prepare_filters_one(info, &options->sub[i]))
+				return 0;
+		}
+		return 1;
+
+	default:
+		error(_("object filter '%s' not supported by the path-walk API"),
+		      list_objects_filter_spec(options));
+		return 0;
+	}
+}
+
+static int prepare_filters(struct path_walk_info *info,
+			   struct list_objects_filter_options *options)
+{
+	if (!prepare_filters_one(info, options))
+		return 0;
+	if (info)
+		list_objects_filter_release(options);
+	return 1;
+}
+
+int path_walk_filter_compatible(struct list_objects_filter_options *options)
+{
+	return prepare_filters(NULL, options);
+}
+
 /**
  * Given the configuration of 'info', walk the commits based on 'info->revs' and
  * call 'info->path_fn' on each discovered path.
@@ -512,6 +715,9 @@ int walk_objects_by_path(struct path_walk_info *info)
 
 	trace2_region_enter("path-walk", "commit-walk", info->revs->repo);
 
+	if (!prepare_filters(info, &info->revs->filter))
+		return -1;
+
 	CALLOC_ARRAY(commit_list, 1);
 	commit_list->type = OBJ_COMMIT;
 
@@ -532,15 +738,17 @@ int walk_objects_by_path(struct path_walk_info *info)
 	push_to_stack(&ctx, root_path);
 
 	/*
-	 * Set these values before preparing the walk to catch
-	 * lightweight tags pointing to non-commits and indexed objects.
+	 * Ensure that prepare_revision_walk() keeps all pending objects
+	 * even through an object type filter.
 	 */
-	info->revs->blob_objects = info->blobs;
-	info->revs->tree_objects = info->trees;
+	info->revs->blob_objects = info->revs->tree_objects = 1;
 
 	if (prepare_revision_walk(info->revs))
 		die(_("failed to setup revision walk"));
 
+	info->revs->blob_objects = info->blobs;
+	info->revs->tree_objects = info->trees;
+
 	/*
 	 * Walk trees to mark them as UNINTERESTING.
 	 * This is particularly important when 'edge_aggressive' is set.
diff --git a/path-walk.h b/path-walk.h
index 5ef5a84..a2652b2 100644
--- a/path-walk.h
+++ b/path-walk.h
@@ -36,6 +36,11 @@ struct path_walk_info {
 	/**
 	 * Initialize which object types the path_fn should be called on. This
 	 * could also limit the walk to skip blobs if not set.
+	 *
+	 * Note: even when 'blobs' or 'trees' is disabled, objects that are
+	 * directly requested as pending objects will still be emitted to
+	 * path_fn. Only objects discovered during the tree walk are filtered by
+	 * these flags.
 	 */
 	int commits;
 	int trees;
@@ -43,6 +48,19 @@ struct path_walk_info {
 	int tags;
 
 	/**
+	 * If 'strict_types' is 0, then direct object requests will no longer
+	 * override the object type restrictions.
+	 */
+	int strict_types;
+
+	/**
+	 * If non-zero, specifies a maximum blob size. Blobs with a
+	 * size equal to or greater than this limit will not be
+	 * emitted unless included in 'pending'.
+	 */
+	unsigned long blob_limit;
+
+	/**
 	 * When 'prune_all_uninteresting' is set and a path has all objects
 	 * marked as UNINTERESTING, then the path-walk will not visit those
 	 * objects. It will not call path_fn on those objects and will not
@@ -64,8 +82,14 @@ struct path_walk_info {
 	 * of the cone. If not in cone mode, then all tree paths will be
 	 * explored but the path_fn will only be called when the path matches
 	 * the sparse-checkout patterns.
+	 *
+	 * When 'pl_sparse_trees' is zero, the sparse patterns only restrict
+	 * blobs and all trees are included in the walk output. This matches
+	 * the behavior of the sparse:oid object filter. When nonzero, trees
+	 * are also pruned by the sparse patterns (as used by backfill).
 	 */
 	struct pattern_list *pl;
+	int pl_sparse_trees;
 };
 
 #define PATH_WALK_INFO_INIT {   \
@@ -85,3 +109,10 @@ void path_walk_info_clear(struct path_walk_info *info);
  * Returns nonzero on an error.
  */
 int walk_objects_by_path(struct path_walk_info *info);
+
+struct list_objects_filter_options;
+/**
+ * Given a set of options for filtering objects, return 1 if the options
+ * are compatible with the path-walk API and 0 otherwise.
+ */
+int path_walk_filter_compatible(struct list_objects_filter_options *options);
diff --git a/pathspec.c b/pathspec.c
index 5993c4a..f78b227 100644
--- a/pathspec.c
+++ b/pathspec.c
@@ -486,7 +486,7 @@ static void init_pathspec_item(struct pathspec_item *item, unsigned flags,
 		match = xstrdup(copyfrom);
 		prefixlen = 0;
 	} else {
-		match = prefix_path_gently(prefix, prefixlen,
+		match = prefix_path_gently(the_repository, prefix, prefixlen,
 					   &prefixlen, copyfrom);
 		if (!match) {
 			const char *hint_path;
diff --git a/pkt-line.h b/pkt-line.h
index 3b33cc6..e6cf85e 100644
--- a/pkt-line.h
+++ b/pkt-line.h
@@ -184,7 +184,7 @@ struct packet_reader {
 	int pktlen;
 
 	/* the last line read */
-	const char *line;
+	char *line;
 
 	/* indicates if a line has been peeked */
 	int line_peeked;
diff --git a/po/.gitattributes b/po/.gitattributes
new file mode 100644
index 0000000..284af6b
--- /dev/null
+++ b/po/.gitattributes
@@ -0,0 +1,35 @@
+# Git Attributes for PO Files
+#
+# This file configures Git filters to automatically strip location information
+# from PO files when committing, producing cleaner diffs and saving repository
+# space.
+#
+# Two filter types are used:
+# 1. gettext-no-location: Strips both filenames and line numbers
+#    (e.g., removes "#: main.c:123" entirely)
+# 2. gettext-no-line-number: Preserves filenames but removes line numbers, which
+#    requires gettext 0.20 or higher
+#    (e.g., "#: main.c:123" becomes "#: main.c")
+#
+# See `po/README.md` for instructions on setting up the required filter drivers.
+
+# Default: Strip the whole location comments for all .po files
+*.po	filter=gettext-no-location
+
+# Legacy, unmaintained PO files: filter disabled to avoid index vs
+# working-tree mismatch (these files still have location comments).
+el.po	-filter
+is.po	-filter
+it.po	-filter
+ko.po	-filter
+pl.po	-filter
+pt_PT.po	-filter
+
+# These files use gettext-no-line-number (keep filenames, strip line
+# numbers). The choice is per l10n team preference. Requires gettext 0.20+.
+# The only benefit is locating source files from location comments when
+# the .po file is not updated from the POT via make po-update.
+ca.po	filter=gettext-no-line-number
+id.po	filter=gettext-no-line-number
+zh_CN.po	filter=gettext-no-line-number
+zh_TW.po	filter=gettext-no-line-number
diff --git a/po/AGENTS.md b/po/AGENTS.md
new file mode 100644
index 0000000..feb90d4
--- /dev/null
+++ b/po/AGENTS.md
@@ -0,0 +1,877 @@
+# Instructions for AI Agents
+
+This file gives specific instructions for AI agents that perform
+housekeeping tasks for Git l10n. Use of AI is optional; many successful
+l10n teams work well without it.
+
+The section "Housekeeping tasks for localization workflows" documents the
+most commonly used housekeeping tasks:
+
+1. Generating or updating po/git.pot
+2. Updating po/XX.po
+3. Translating po/XX.po
+4. Reviewing translation quality
+
+
+## Background knowledge for localization workflows
+
+Essential background for the workflows below; understand these concepts before
+performing any housekeeping tasks in this document.
+
+### Language code and notation (XX, ll, ll\_CC)
+
+**XX** is a placeholder for the language code: either `ll` (ISO 639) or
+`ll_CC` (e.g. `de`, `zh_CN`). It appears in the PO file header metadata
+(e.g. `"Language: zh_CN\n"`) and is typically used to name the PO file:
+`po/XX.po`.
+
+
+### Header Entry
+
+The **header entry** is the first entry in every `po/XX.po`. It has an empty
+`msgid`; translation metadata (project, language, plural rules, encoding, etc.)
+is stored in `msgstr`, as in this example:
+
+```po
+msgid ""
+msgstr ""
+"Project-Id-Version: Git\n"
+"Language: zh_CN\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+```
+
+**CRITICAL**: Do not edit the header's `msgstr` while translating. It holds
+metadata only and must be left unchanged.
+
+
+### Glossary Section
+
+PO files may have a glossary in comments before the header entry (first
+`msgid ""`), giving terminology guidelines (e.g.):
+
+```po
+# Git glossary for Chinese translators
+#
+#   English                          |  Chinese
+#   ---------------------------------+--------------------------------------
+#   3-way merge                      |  三路合并
+#   ...
+```
+
+**IMPORTANT**: Read and use the glossary when translating or reviewing. It is
+in `#` comments only. Leave that comment block unchanged.
+
+
+### PO entry structure (single-line and multi-line)
+
+PO entries are `msgid` / `msgstr` pairs. Plural messages add `msgid_plural` and
+`msgstr[n]`. The `msgid` is the immutable source; `msgstr` is the target
+translation. Each side may be a single quoted string or a multi-line block.
+In the multi-line form the header line is often `msgid ""` / `msgstr ""`, with
+the real text split across following quoted lines (concatenated by Gettext).
+
+**Single-line entries**:
+
+```po
+msgid "commit message"
+msgstr "提交说明"
+```
+
+**Multi-line entries**:
+
+```po
+msgid ""
+"Line 1\n"
+"Line 2"
+msgstr ""
+"行 1\n"
+"行 2"
+```
+
+**CRITICAL**: Do **not** use `grep '^msgstr ""'` to find untranslated entries;
+multi-line `msgstr` blocks use the same opening line, so grep gives false
+positives. Use `msgattrib` (next section).
+
+
+### Locating untranslated, fuzzy, and obsolete entries
+
+Use `msgattrib` to list untranslated, fuzzy, and obsolete entries. Task 3
+(translating `po/XX.po`) uses these commands.
+
+- **Untranslated**: `msgattrib --untranslated --no-obsolete po/XX.po`
+- **Fuzzy**: `msgattrib --only-fuzzy --no-obsolete po/XX.po`
+- **Obsolete** (`#~`): `msgattrib --obsolete --no-wrap po/XX.po`
+
+
+### Translating fuzzy entries
+
+Fuzzy entries need re-translation because the source text changed. The format
+differs by file type:
+
+- **PO file**: A `#, fuzzy` tag in the entry comments marks the entry as fuzzy.
+- **JSON file**: The entry has `"fuzzy": true`.
+
+**Translation principles**: Re-translate the `msgstr` (and, for plural entries,
+`msgstr[n]`) into the target language. Do **not** modify `msgid` or
+`msgid_plural`. After translation, **clear the fuzzy mark**: in PO, remove the
+`#, fuzzy` tag from comments; in JSON, omit or set `fuzzy` to `false`.
+
+
+### Preserving Special Characters
+
+Preserve escape sequences (`\n`, `\"`, `\\`, `\t`), placeholders (`%s`, `%d`,
+etc.), and quotes exactly as in `msgid`. Only reorder placeholders with
+positional syntax when needed (see Placeholder Reordering below).
+
+
+### Placeholder Reordering
+
+When reordering placeholders relative to `msgid`, use positional syntax (`%n$`)
+where *n* is the 1-based argument index, so each argument still binds to the
+right value. Preserve width and precision modifiers, and place `%n$` before
+them (see examples below).
+
+**Example 1** (placeholder reordering with precision):
+
+```po
+msgid "missing environment variable '%s' for configuration '%.*s'"
+msgstr "配置 '%3$.*2$s' 缺少环境变量 '%1$s'"
+```
+
+`%s` → argument 1 → `%1$s`. `%.*s` needs precision (arg 2) and string (arg 3) →
+`%3$.*2$s`.
+
+**Example 2** (multi-line, four `%s` reordered):
+
+```po
+msgid ""
+"Path updated: %s renamed to %s in %s, inside a directory that was renamed in "
+"%s; moving it to %s."
+msgstr ""
+"路径已更新:%1$s 在 %3$s 中被重命名为 %2$s,而其所在目录又在 %4$s 中被重命"
+"名,因此将其移动到 %5$s。"
+```
+
+Original order 1,2,3,4,5; in translation 1,3,2,4,5. Each line must be a
+complete quoted string.
+
+**Example 3** (no placeholder reordering):
+
+```po
+msgid "MIDX %s must be an ancestor of %s"
+msgstr "MIDX %s 必须是 %s 的祖先"
+```
+
+Argument order is still 1,2 in translation, so `%n$` is not needed.
+If no placeholder reordering occurs, you **must not** introduce `%n$`
+syntax; keep the original non-positional placeholders (`%s`, `%d`, etc.).
+
+
+### Validating PO File Format
+
+Check the PO file using the command below:
+
+```shell
+msgfmt --check -o /dev/null po/XX.po
+```
+
+Common validation errors include:
+- Unclosed quotes
+- Missing escape sequences
+- Invalid placeholder syntax
+- Malformed multi-line entries
+- Incorrect line breaks in multi-line strings
+
+On failure, `msgfmt` prints the line number; fix the PO at that line.
+
+
+### Using git-po-helper
+
+[git-po-helper](https://github.com/git-l10n/git-po-helper) supports Git l10n with
+**quality checking** (git-l10n PR conventions) and **AI-assisted translation**
+(subcommands for automated workflows). Housekeeping tasks in this document use
+it when available; otherwise rely on gettext tools.
+
+
+#### Splitting large PO files
+
+When a PO file is too large for translation or review, use `git-po-helper
+msg-select` to split it by entry index.
+
+- **Entry 0** is the header (included by default; use `--no-header` to omit).
+- **Entries 1, 2, 3, …** are content entries.
+- **Range format**: `--range "1-50"` (entries 1 through 50), `--range "-50"`
+  (first 50 entries), `--range "51-"` (from entry 51 to end). Shortcuts:
+  `--head N` (first N), `--tail N` (last N), `--since N` (from N to end).
+- **Output format**: PO by default; use `--json` for GETTEXT JSON. See the
+  "GETTEXT JSON format" section (under git-po-helper) for details.
+- **State filter**: Use `--translated`, `--untranslated`, `--fuzzy` to filter
+  by state (OR relationship). Use `--no-obsolete` to exclude obsolete entries;
+  `--with-obsolete` to include (default). Use `--only-same` or `--only-obsolete`
+  for a single state. Range applies to the filtered list.
+
+```shell
+# First 50 entries (header + entries 1–50)
+git-po-helper msg-select --range "-50" po/in.po -o po/out.po
+
+# Entries 51–100
+git-po-helper msg-select --range "51-100" po/in.po -o po/out.po
+
+# Entries 101 to end
+git-po-helper msg-select --range "101-" po/in.po -o po/out.po
+
+# Entries 1–50 without header (content only)
+git-po-helper msg-select --range "1-50" --no-header po/in.po -o po/frag.po
+
+# Output as JSON; select untranslated and fuzzy entries, exclude obsolete
+git-po-helper msg-select --json --untranslated --fuzzy --no-obsolete po/in.po >po/filtered.json
+```
+
+
+#### Comparing PO files for translation and review
+
+`git-po-helper compare` shows PO changes with full entry context (unlike
+`git diff`). Redirect output to a file: it is empty when there are no new or
+changed entries; otherwise it contains a valid PO header.
+
+```shell
+# Get full context of local changes (HEAD vs working tree)
+git-po-helper compare po/XX.po -o po/out.po
+
+# Get full context of changes in a specific commit (parent vs commit)
+git-po-helper compare --commit <commit> po/XX.po -o po/out.po
+
+# Get full context of changes since a commit (commit vs working tree)
+git-po-helper compare --since <commit> po/XX.po -o po/out.po
+
+# Get full context between two commits
+git-po-helper compare -r <commit1>..<commit2> po/XX.po -o po/out.po
+
+# Get full context of two worktree files
+git-po-helper compare po/old.po po/new.po -o po/out.po
+
+# Check msgid consistency (detect tampering); no output means target matches source
+git-po-helper compare --msgid po/old.po po/new.po >po/out.po
+```
+
+**Options summary**
+
+| Option              | Meaning                                        |
+|---------------------|------------------------------------------------|
+| (none)              | Compare HEAD with working tree (local changes) |
+| `--commit <commit>` | Compare parent of commit with the commit       |
+| `--since <commit>`  | Compare commit with working tree               |
+| `-r x..y`           | Compare revision x with revision y             |
+| `-r x..`            | Compare revision x with working tree           |
+| `-r x`              | Compare parent of x with x                     |
+
+
+#### Concatenating multiple PO/JSON files
+
+`git-po-helper msg-cat` merges PO, POT, or gettext JSON inputs into one stream.
+Duplicate `msgid` values keep the first occurrence in file order. Write with
+`-o <file>` or stdout (`-o -` or omit); `--json` selects JSON output, else PO.
+
+```shell
+# Convert JSON to PO (e.g. after translation)
+git-po-helper msg-cat --unset-fuzzy -o po/out.po po/in.json
+
+# Merge multiple PO files
+git-po-helper msg-cat -o po/out.po po/in-1.po po/in-2.json
+```
+
+
+#### GETTEXT JSON format
+
+The **GETTEXT JSON** format is an internal format defined by `git-po-helper`
+for convenient batch processing of translation and related tasks by AI models.
+`git-po-helper msg-select`, `git-po-helper msg-cat`, and `git-po-helper compare`
+read and write this format.
+
+**Top-level structure**:
+
+```json
+{
+  "header_comment": "string",
+  "header_meta": "string",
+  "entries": [ /* array of entry objects */ ]
+}
+```
+
+| Field            | Description                                                                    |
+|------------------|--------------------------------------------------------------------------------|
+| `header_comment` | Lines above the first `msgid ""` (comments, glossary), directly concatenated.  |
+| `header_meta`    | Encoded `msgstr` of the header entry (Project-Id-Version, Plural-Forms, etc.). |
+| `entries`        | List of PO entries. Order matches source.                                      |
+
+**Entry object** (each element of `entries`):
+
+| Field           | Type     | Description                                                  |
+|-----------------|----------|--------------------------------------------------------------|
+| `msgid`         | string   | Singular message ID. PO escapes encoded (e.g. `\n` → `\\n`). |
+| `msgstr`        | []string | Translation forms as a **JSON array only**. Details below.   |
+| `msgid_plural`  | string   | Plural form of msgid. Omit for non-plural.                   |
+| `comments`      | []string | Comment lines (`#`, `#.`, `#:`, `#,`, etc.).                 |
+| `fuzzy`         | bool     | True if entry has fuzzy flag.                                |
+| `obsolete`      | bool     | True for `#~` obsolete entries. Omit if false.               |
+
+**`msgstr` array (required shape)**:
+
+- **Always** a JSON array of strings, never a single string. One element = singular
+  (PO `msgstr` / `msgstr[0]`); multiple elements = plural forms in order
+  (`msgstr[0]`, `msgstr[1]`, …).
+- Omit the key or use an empty array when the entry is untranslated.
+
+**Example (single-line entry)**:
+
+```json
+{
+  "header_comment": "# Glossary:\\n# term1\\tTranslation 1\\n#\\n",
+  "header_meta": "Project-Id-Version: git\\nContent-Type: text/plain; charset=UTF-8\\n",
+  "entries": [
+    {
+      "msgid": "Hello",
+      "msgstr": ["你好"],
+      "comments": ["#. Comment for translator\\n", "#: src/file.c:10\\n"],
+      "fuzzy": false
+    }
+  ]
+}
+```
+
+**Example (plural entry)**:
+
+```json
+{
+  "msgid": "One file",
+  "msgid_plural": "%d files",
+  "msgstr": ["一个文件", "%d 个文件"],
+  "comments": ["#, c-format\\n"]
+}
+```
+
+**Example (fuzzy entry before translation)**:
+
+```json
+{
+  "msgid": "Old message",
+  "msgstr": ["旧翻译。"],
+  "comments": ["#, fuzzy\\n"],
+  "fuzzy": true
+}
+```
+
+**Translation notes for GETTEXT JSON files**:
+
+- **Preserve structure**: Keep `header_comment`, `header_meta`, `msgid`,
+  `msgid_plural` unchanged.
+- **Fuzzy entries**: Entries extracted from fuzzy PO entries have `"fuzzy": true`.
+  After translating, **remove the `fuzzy` field** or set it to `false` in the
+  output JSON. The merge step uses `--unset-fuzzy`, which can also remove the
+  `fuzzy` field.
+- **Placeholders**: Preserve `%s`, `%d`, etc. exactly; use `%n$` when
+  reordering (see "Placeholder Reordering" above).
+
+
+### Quality checklist
+
+- **Accuracy**: Faithful to original meaning; no omissions or distortions.
+- **Fuzzy entries**: Re-translate fully and clear the fuzzy flag (see
+  "Translating fuzzy entries" above).
+- **Terminology**: Consistent with glossary (see "Glossary Section" above) or
+  domain standards.
+- **Grammar and fluency**: Correct and natural in the target language.
+- **Placeholders**: Preserve variables (`%s`, `{name}`, `$1`) exactly; use
+  positional parameters when reordering (see "Placeholder Reordering" above).
+- **Special characters**: Preserve escape sequences (`\n`, `\"`, `\\`, `\t`),
+  placeholders exactly as in `msgid`. See "Preserving Special Characters" above.
+- **Plurals and gender**: Correct forms and agreement.
+- **Context fit**: Suitable for UI space, tone, and use (e.g. error vs. tooltip).
+- **Cultural appropriateness**: No offensive or ambiguous content.
+- **Consistency**: Match prior translations of the same source.
+- **Technical integrity**: Do not translate code, paths, commands, brands, or
+  proper nouns.
+- **Readability**: Clear, concise, and user-friendly.
+
+
+## Housekeeping tasks for localization workflows
+
+For common housekeeping tasks, follow the steps in the matching subsection
+below.
+
+
+### Task 1: Generating or updating po/git.pot
+
+When asked to generate or update `po/git.pot` (or the like):
+
+1. **Directly execute** the command `make po/git.pot` without checking
+   if the file exists beforehand.
+
+2. **Do not verify** the generated file after execution. Simply run the
+   command and consider the task complete.
+
+
+### Task 2: Updating po/XX.po
+
+When asked to update `po/XX.po` (or the like):
+
+1. **Directly execute** the command `make po-update PO_FILE=po/XX.po`
+   without reading or checking the file content beforehand.
+
+2. **Do not verify, translate, or review** the updated file after execution.
+   Simply run the command and consider the task complete.
+
+
+### Task 3: Translating po/XX.po
+
+To translate `po/XX.po`, use the steps below. The script uses gettext or
+`git-po-helper` depending on what is installed; JSON export (when available)
+supports batch translation rather than per-entry work.
+
+**Workflow loop**: Steps 1→2→3→4→5→6→7 form a loop. After step 6 succeeds,
+**always** go to step 7, which returns to step 1. The **only** exit to step 8
+is when step 2 finds `po/l10n-pending.po` empty. Do not skip step 7 or jump to
+step 8 after step 6.
+
+1. **Extract entries to translate**: **Directly execute** the script below—it is
+   authoritative; do not reimplement. It generates `po/l10n-pending.po` with
+   messages that need translation.
+
+   ```shell
+   l10n_extract_pending () {
+       test $# -ge 1 || { echo "Usage: l10n_extract_pending <po-file>" >&2; return 1; }
+       PO_FILE="$1"
+       PENDING="po/l10n-pending.po"
+       PENDING_FUZZY="${PENDING}.fuzzy"
+       PENDING_REFER="${PENDING}.fuzzy.reference"
+       PENDING_UNTRANS="${PENDING}.untranslated"
+       rm -f "$PENDING"
+
+       if command -v git-po-helper >/dev/null 2>&1
+       then
+           git-po-helper msg-select --untranslated --fuzzy --no-obsolete -o "$PENDING" "$PO_FILE"
+       else
+           msgattrib --untranslated --no-obsolete "$PO_FILE" >"${PENDING_UNTRANS}"
+           msgattrib --only-fuzzy --no-obsolete --clear-fuzzy --empty "$PO_FILE" >"${PENDING_FUZZY}"
+           msgattrib --only-fuzzy --no-obsolete "$PO_FILE" >"${PENDING_REFER}"
+           msgcat --use-first "${PENDING_UNTRANS}" "${PENDING_FUZZY}" >"$PENDING"
+           rm -f "${PENDING_UNTRANS}" "${PENDING_FUZZY}"
+       fi
+       if test -s "$PENDING"
+       then
+           msgfmt --stat -o /dev/null "$PENDING" || true
+           echo "Pending file is not empty; there are still entries to translate."
+       else
+           echo "No entries need translation."
+           return 1
+       fi
+   }
+   # Run the extraction. Example: l10n_extract_pending po/zh_CN.po
+   l10n_extract_pending po/XX.po
+   ```
+
+2. **Check generated file**: If `po/l10n-pending.po` is empty or does not exist,
+   translation is complete; go to step 8. Otherwise proceed to step 3.
+
+3. **Prepare one batch for translation**: Batching keeps each run small so the
+   model can complete translation within limited context. **BEFORE translating**,
+   **directly execute** the script below—it is authoritative; do not reimplement.
+   Based on which file the script produces: if `po/l10n-todo.json` exists, go to
+   step 4a; if `po/l10n-todo.po` exists, go to step 4b.
+
+   ```shell
+   l10n_one_batch () {
+       test $# -ge 1 || { echo "Usage: l10n_one_batch <po-file> [min_batch_size]" >&2; return 1; }
+       PO_FILE="$1"
+       min_batch_size=${2:-100}
+       PENDING="po/l10n-pending.po"
+       TODO_JSON="po/l10n-todo.json"
+       TODO_PO="po/l10n-todo.po"
+       DONE_JSON="po/l10n-done.json"
+       DONE_PO="po/l10n-done.po"
+       rm -f "$TODO_JSON" "$TODO_PO" "$DONE_JSON" "$DONE_PO"
+
+       ENTRY_COUNT=$(grep -c '^msgid ' "$PENDING" 2>/dev/null || echo 0)
+       ENTRY_COUNT=$((ENTRY_COUNT > 0 ? ENTRY_COUNT - 1 : 0))
+
+       if test "$ENTRY_COUNT" -gt $min_batch_size
+       then
+           if test "$ENTRY_COUNT" -gt $((min_batch_size * 8))
+           then
+               NUM=$((min_batch_size * 2))
+           elif test "$ENTRY_COUNT" -gt $((min_batch_size * 4))
+           then
+               NUM=$((min_batch_size + min_batch_size / 2))
+           else
+               NUM=$min_batch_size
+           fi
+           BATCHING=1
+       else
+           NUM=$ENTRY_COUNT
+           BATCHING=
+       fi
+
+       if command -v git-po-helper >/dev/null 2>&1
+       then
+           if test -n "$BATCHING"
+           then
+               git-po-helper msg-select --json --head "$NUM" -o "$TODO_JSON" "$PENDING"
+               echo "Processing batch of $NUM entries (out of $ENTRY_COUNT remaining)"
+           else
+               git-po-helper msg-select --json -o "$TODO_JSON" "$PENDING"
+               echo "Processing all $ENTRY_COUNT entries at once"
+           fi
+       else
+           if test -n "$BATCHING"
+           then
+               awk -v num="$NUM" '/^msgid / && count++ > num {exit} 1' "$PENDING" |
+                   tac | awk '/^$/ {found=1} found' | tac >"$TODO_PO"
+               echo "Processing batch of $NUM entries (out of $ENTRY_COUNT remaining)"
+           else
+               cp "$PENDING" "$TODO_PO"
+               echo "Processing all $ENTRY_COUNT entries at once"
+           fi
+       fi
+   }
+   # Prepare one batch; shrink 2nd arg when batches exceed agent capacity.
+   l10n_one_batch po/XX.po 100
+   ```
+
+4a. **Translate JSON batch** (`po/l10n-todo.json` → `po/l10n-done.json`):
+
+   - **Task**: Translate `po/l10n-todo.json` (input, GETTEXT JSON) into
+     `po/l10n-done.json` (output, GETTEXT JSON). See the "GETTEXT JSON format"
+     section above for format details and translation rules.
+   - **Reference glossary**: Read the glossary from the batch file's
+     `header_comment` (see "Glossary Section" above) and use it for
+     consistent terminology.
+   - **When translating**: Follow the "Quality checklist" above for correctness
+     and quality. Handle escape sequences (`\n`, `\"`, `\\`, `\t`), placeholders,
+     and quotes correctly as in `msgid`. For JSON, correctly escape and unescape
+     these sequences when reading and writing. Modify `msgstr` and `msgstr[n]`
+     (for plural entries); clear the fuzzy flag (omit or set `fuzzy` to `false`).
+     Do **not** modify `msgid` or `msgid_plural`.
+
+4b. **Translate PO batch** (`po/l10n-todo.po` → `po/l10n-done.po`):
+
+   - **Task**: Translate `po/l10n-todo.po` (input, GETTEXT PO) into
+     `po/l10n-done.po` (output, GETTEXT PO).
+   - **Reference glossary**: Read the glossary from the pending file header
+     (see "Glossary Section" above) and use it for consistent terminology.
+   - **When translating**: Follow the "Quality checklist" above for correctness
+     and quality. Preserve escape sequences (`\n`, `\"`, `\\`, `\t`), placeholders,
+     and quotes as in `msgid`. Modify `msgstr` and `msgstr[n]` (for plural
+     entries); remove the `#, fuzzy` tag from comments when done. Do **not**
+     modify `msgid` or `msgid_plural`.
+
+5. **Validate `po/l10n-done.po`**:
+
+   Run the validation script below. If it fails, fix per the errors and notes,
+   re-run until it succeeds.
+
+   ```shell
+   l10n_validate_done () {
+       DONE_PO="po/l10n-done.po"
+       DONE_JSON="po/l10n-done.json"
+       PENDING="po/l10n-pending.po"
+
+       if test -f "$DONE_JSON" && { ! test -f "$DONE_PO" || test "$DONE_JSON" -nt "$DONE_PO"; }
+       then
+           git-po-helper msg-cat --unset-fuzzy -o "$DONE_PO" "$DONE_JSON" || {
+               echo "ERROR [JSON to PO conversion]: Fix $DONE_JSON and re-run." >&2
+               return 1
+           }
+       fi
+
+       # Check 1: msgid should not be modified
+       MSGID_OUT=$(git-po-helper compare -q --msgid --assert-no-changes \
+           "$PENDING" "$DONE_PO" 2>&1)
+       MSGID_RC=$?
+       if test $MSGID_RC -ne 0 || test -n "$MSGID_OUT"
+       then
+           echo "ERROR [msgid modified]: The following entries appeared after" >&2
+           echo "translation because msgid was altered. Fix in $DONE_PO." >&2
+           echo "$MSGID_OUT" >&2
+           return 1
+       fi
+
+       # Check 2: PO format (see "Validating PO File Format" for error handling)
+       MSGFMT_OUT=$(msgfmt --check -o /dev/null "$DONE_PO" 2>&1)
+       MSGFMT_RC=$?
+       if test $MSGFMT_RC -ne 0
+       then
+           echo "ERROR [PO format]: Fix errors in $DONE_PO." >&2
+           echo "$MSGFMT_OUT" >&2
+           return 1
+       fi
+
+       echo "Validation passed."
+   }
+   l10n_validate_done
+   ```
+
+   If the script fails, fix **directly in `po/l10n-done.po`**. Re-run
+   `l10n_validate_done` until it succeeds. Editing `po/l10n-done.json` is not
+   recommended because it adds an extra JSON-to-PO conversion step. Use the
+   error message to decide:
+
+   - **`[msgid modified]`**: The listed entries have altered `msgid`; restore
+     them to match `po/l10n-pending.po`.
+   - **`[PO format]`**: `msgfmt` reports line numbers; fix the errors in place.
+     See "Validating PO File Format" for common issues.
+
+
+6. **Merge translation results into `po/XX.po`**: Run the script below. If it
+   fails, fix the file the error names: **`[JSON to PO conversion]`** →
+   `po/l10n-done.json`; **`[msgcat merge]`** → `po/l10n-done.po`. Re-run until
+   it succeeds.
+
+   ```shell
+   l10n_merge_batch () {
+       test $# -ge 1 || { echo "Usage: l10n_merge_batch <po-file>" >&2; return 1; }
+       PO_FILE="$1"
+       DONE_PO="po/l10n-done.po"
+       DONE_JSON="po/l10n-done.json"
+       MERGED="po/l10n-done.merged"
+       PENDING="po/l10n-pending.po"
+       PENDING_REFER="${PENDING}.fuzzy.reference"
+       TODO_JSON="po/l10n-todo.json"
+       TODO_PO="po/l10n-todo.po"
+       if test -f "$DONE_JSON" && { ! test -f "$DONE_PO" || test "$DONE_JSON" -nt "$DONE_PO"; }
+       then
+           git-po-helper msg-cat --unset-fuzzy -o "$DONE_PO" "$DONE_JSON" || {
+               echo "ERROR [JSON to PO conversion]: Fix $DONE_JSON and re-run." >&2
+               return 1
+           }
+       fi
+       msgcat --use-first "$DONE_PO" "$PO_FILE" >"$MERGED" || {
+           echo "ERROR [msgcat merge]: Fix errors in $DONE_PO and re-run." >&2
+           return 1
+       }
+       mv "$MERGED" "$PO_FILE"
+       rm -f "$TODO_JSON" "$TODO_PO" "$DONE_JSON" "$DONE_PO" "$PENDING_REFER"
+   }
+   # Run the merge. Example: l10n_merge_batch po/zh_CN.po
+   l10n_merge_batch po/XX.po
+   ```
+
+7. **Loop**: **MUST** return to step 1 (Extract entries) and repeat the cycle.
+   Do **not** skip this step or go to step 8. Step 8 (below) runs **only**
+   when step 2 finds no more entries and redirects there.
+
+8. **Only after loop exits**: Run the command below to validate the PO file and
+   display the report. The process ends here.
+
+   ```shell
+   msgfmt --check --stat -o /dev/null po/XX.po
+   ```
+
+
+### Task 4: Review translation quality
+
+Review may target the full `po/XX.po`, a specific commit, or changes since a
+commit. When asked to review, follow the steps below.
+
+**Workflow**: Follow steps in order. Do **NOT** use `git show`, `git diff`,
+`git format-patch`, or similar to get changes—they break PO context; use **only**
+`git-po-helper compare` for extraction. Without `git-po-helper`, refuse the task.
+Steps 3→4→5→6→7 loop: after step 6, **always** go to step 7 (back to step 3).
+The **only** ways to step 8 are when step 4 finds `po/review-todo.json` missing
+or empty (no batch left to review), or when step 1 finds `po/review-result.json`
+already present.
+
+1. **Check for existing review (resume support)**: Evaluate the following in order:
+
+   - If `po/review-input.po` does **not** exist, proceed to step 2 (Extract
+     entries) for a fresh start.
+   - Else If `po/review-result.json` exists, go to step 8 (only after loop exits).
+   - Else If `po/review-done.json` exists, go to step 6 (Rename result).
+   - Else if `po/review-todo.json` exists, go to step 5 (Review the current
+     batch).
+   - Else go to step 3 (Prepare one batch).
+
+2. **Extract entries**: Run `git-po-helper compare` with the desired range and
+   redirect the output to `po/review-input.po`. See "Comparing PO files for
+   translation and review" under git-po-helper for options.
+
+3. **Prepare one batch**: Batching keeps each run small so the model can
+   complete review within limited context. **Directly execute** the script
+   below—it is authoritative; do not reimplement.
+
+   ```shell
+   review_one_batch () {
+       min_batch_size=${1:-100}
+       INPUT_PO="po/review-input.po"
+       PENDING="po/review-pending.po"
+       TODO="po/review-todo.json"
+       DONE="po/review-done.json"
+       BATCH_FILE="po/review-batch.txt"
+
+       if test ! -f "$INPUT_PO"
+       then
+           rm -f "$TODO"
+           echo >&2 "cannot find $INPUT_PO, nothing for review"
+           return 1
+       fi
+       if test ! -f "$PENDING" || test "$INPUT_PO" -nt "$PENDING"
+       then
+           rm -f "$BATCH_FILE" "$TODO" "$DONE"
+           rm -f po/review-result*.json
+           cp "$INPUT_PO" "$PENDING"
+       fi
+
+       ENTRY_COUNT=$(grep -c '^msgid ' "$PENDING" 2>/dev/null || echo 0)
+       ENTRY_COUNT=$((ENTRY_COUNT > 0 ? ENTRY_COUNT - 1 : 0))
+       if test "$ENTRY_COUNT" -eq 0
+       then
+           rm -f "$TODO"
+           echo >&2 "No entries left for review"
+           return 1
+       fi
+
+       if test "$ENTRY_COUNT" -gt $min_batch_size
+       then
+           if test "$ENTRY_COUNT" -gt $((min_batch_size * 8))
+           then
+               NUM=$((min_batch_size * 2))
+           elif test "$ENTRY_COUNT" -gt $((min_batch_size * 4))
+           then
+               NUM=$((min_batch_size + min_batch_size / 2))
+           else
+               NUM=$min_batch_size
+           fi
+       else
+           NUM=$ENTRY_COUNT
+       fi
+
+       BATCH=$(cat "$BATCH_FILE" 2>/dev/null || echo 0)
+       BATCH=$((BATCH + 1))
+       echo "$BATCH" >"$BATCH_FILE"
+
+       git-po-helper msg-select --json --head "$NUM" -o "$TODO" "$PENDING"
+       git-po-helper msg-select --since "$((NUM + 1))" -o "${PENDING}.tmp" "$PENDING"
+       mv "${PENDING}.tmp" "$PENDING"
+       echo "Processing batch $BATCH ($NUM entries out of $ENTRY_COUNT)"
+   }
+   # The parameter controls batch size; reduce if the batch file is too large.
+   review_one_batch 100
+   ```
+
+4. **Check todo file**: If `po/review-todo.json` does not exist or is empty,
+   review is complete; go to step 8 (only after loop exits). Otherwise proceed to
+   step 5.
+
+5. **Review the current batch**: Review translations in `po/review-todo.json`
+   and write findings to `po/review-done.json` as follows:
+   - Use "Background knowledge for localization workflows" for PO/JSON structure,
+     placeholders, and terminology.
+   - If `header_comment` includes a glossary, follow it for consistency.
+   - Do **not** review the header (`header_comment`, `header_meta`).
+   - For every other entry, check the entry's `msgstr` **array** (translation
+     forms) against `msgid` / `msgid_plural` using the "Quality checklist" above.
+   - Write JSON per "Review result JSON format" below; use `{"issues": []}` when
+     there are no issues. **Always** write `po/review-done.json`—it marks the
+     batch complete.
+
+6. **Rename result**: Rename `po/review-done.json` to `po/review-result-<N>.json`,
+   where N is the value in `po/review-batch.txt` (the batch just completed).
+   Run the script below:
+
+   ```shell
+   review_rename_result () {
+       TODO="po/review-todo.json"
+       DONE="po/review-done.json"
+       BATCH_FILE="po/review-batch.txt"
+       if test -f "$DONE"
+       then
+           N=$(cat "$BATCH_FILE" 2>/dev/null) || { echo "ERROR: $BATCH_FILE not found." >&2; return 1; }
+           mv "$DONE" "po/review-result-$N.json"
+           echo "Renamed to po/review-result-$N.json"
+       fi
+       rm -f "$TODO"
+   }
+   review_rename_result
+   ```
+
+7. **Loop**: **MUST** return to step 3 (Prepare one batch) and repeat the cycle.
+   Do **not** skip this step or go to step 8. Step 8 is reached **only** when
+   step 4 finds `po/review-todo.json` missing or empty.
+
+8. **Only after loop exits**: **Directly execute** the command below. It merges
+   results, applies suggestions, and displays the report. The process ends here.
+
+   ```shell
+   git-po-helper agent-run review --report po
+   ```
+
+   **Do not** run cleanup or delete intermediate files. Keep them for inspection
+   or resumption.
+
+**Review result JSON format**:
+
+The **Review result JSON** format defines the structure for translation
+review reports. For each entry with translation issues, create an issue
+object as follows:
+
+- Copy the original entry's `msgid`, optional `msgid_plural`, and optional
+  `msgstr` array (original translation forms) into the issue object. Use the
+  same shape as GETTEXT JSON: `msgstr` is **always a JSON array** when present
+  (one element singular, multiple for plural).
+- Write a summary of all issues found for this entry in `description`.
+- Set `score` according to the severity of issues found for this entry,
+  from 0 to 3 (0 = critical; 1 = major; 2 = minor; 3 = perfect, no issues).
+  **Lower score means more severe issues.**
+- Place the suggested translation in **`suggest_msgstr`** as a **JSON array**:
+  one string for singular, multiple strings for plural forms in order. This is
+  required for `git-po-helper` to apply suggestions.
+- Include only entries with issues (score less than 3). When no issues are
+  found in the batch, write `{"issues": []}`.
+
+Example review result (with issues):
+
+```json
+{
+  "issues": [
+    {
+      "msgid": "commit",
+      "msgstr": ["委托"],
+      "score": 0,
+      "description": "Terminology error: 'commit' should be translated as '提交'",
+      "suggest_msgstr": ["提交"]
+    },
+    {
+      "msgid": "repository",
+      "msgid_plural": "repositories",
+      "msgstr": ["版本库", "版本库"],
+      "score": 2,
+      "description": "Consistency issue: suggest using '仓库' consistently",
+      "suggest_msgstr": ["仓库", "仓库"]
+    }
+  ]
+}
+```
+
+Field descriptions for each issue object (element of the `issues` array):
+
+- `msgid` (and optional `msgid_plural` for plural entries): Original source text.
+- `msgstr` (optional): JSON array of original translation forms (same meaning as
+  in GETTEXT JSON entries).
+- `suggest_msgstr`: JSON array of suggested translation forms; **must be an
+  array** (e.g. `["提交"]` for singular). Plural entries use multiple elements
+  in order.
+- `score`: 0–3 (0 = critical; 1 = major; 2 = minor; 3 = perfect, no issues).
+- `description`: Brief summary of the issue.
+
+
+## Human translators remain in control
+
+Git translation is human-driven; language team leaders and contributors are
+responsible for maintaining translation quality and consistency.
+
+AI-generated output should always be treated as drafts that must be reviewed
+and approved by someone who understands both the technical context and the
+target language. The best results come from combining AI efficiency with human
+judgment, cultural insight, and community engagement.
diff --git a/po/README.md b/po/README.md
index ec08aa2..c418487 100644
--- a/po/README.md
+++ b/po/README.md
@@ -159,38 +159,6 @@
   and these location lines will help translation tools to locate
   translation context easily.
 
-Once you are done testing the translation (see below), it's better
-to commit a location-less "po/XX.po" file to save repository space
-and make a user-friendly patch for review.
-
-To save a location-less "po/XX.po" automatically in repository, you
-can:
-
-First define a new attribute for "po/XX.po" by appending the following
-line in ".git/info/attributes":
-
-```
-/po/XX.po filter=gettext-no-location
-```
-
-Then define the driver for the "gettext-no-location" clean filter to
-strip out both filenames and locations from the contents as follows:
-
-```shell
-git config --global filter.gettext-no-location.clean \
-           "msgcat --no-location -"
-```
-
-For users who have gettext version 0.20 or higher, it is also possible
-to define a clean filter to preserve filenames but not locations:
-
-```shell
-git config --global filter.gettext-no-location.clean \
-           "msgcat --add-location=file -"
-```
-
-You're now ready to ask the l10n coordinator to pull from you.
-
 
 ## Fuzzy translation
 
@@ -229,6 +197,45 @@
 ```
 
 
+## Preparing a "XX.po" file for commit
+
+Once you are done testing the translation, it's better to commit a
+location-less "po/XX.po" file to save repository space and make a
+user-friendly patch for review.
+
+To save a location-less "po/XX.po" automatically in the repository,
+follow these steps:
+
+First, check which filter is configured for your "po/XX.po" file:
+
+```
+git check-attr filter po/XX.po
+```
+
+The filter configuration is defined in the "po/.gitattributes" file.
+
+Then define the driver for the filter. Most languages use the
+"gettext-no-location" clean filter, which strips out both filenames and line
+numbers from location comments. To set this up, run the following command:
+
+```shell
+git config --global filter.gettext-no-location.clean \
+           "msgcat --no-location -"
+```
+
+Some PO files use the "gettext-no-line-number" clean filter, which keeps
+filenames but strips line numbers. This filter requires gettext 0.20 or
+later. The only benefit is being able to locate source files from location
+comments when the .po file is not updated from the POT via `make po-update`.
+
+```shell
+git config --global filter.gettext-no-line-number.clean \
+           "msgcat --add-location=file -"
+```
+
+You're now ready to ask the l10n coordinator to pull from you.
+
+
 ## Marking strings for translation
 
 (This is done by the core developers).
@@ -392,15 +399,30 @@
 changed to account for translations as they're added.
 
 
+## AI-assisted translation and review
+
+[po/AGENTS.md](AGENTS.md) describes optional workflows for AI coding assistants
+that help with Git localization: updating templates and PO files, translating
+`po/XX.po`, and reviewing translations. Those workflows often use git-po-helper
+together with the gettext tools; see the PO helper section below for what the
+program does and how to build or install it. AI assistants are optional; treat
+their output as a draft and have it reviewed by contributors who know Git and
+the target language well.
+
+When you prompt a coding assistant, mention that file explicitly, for example:
+"Translate po/XX.po with reference to po/AGENTS.md" (replace XX with your
+language code).
+
+
 ## PO helper
 
-To make the maintenance of "XX.po" easier, the l10n coordinator and l10n
-team leaders can use a helper program named "git-po-helper". It is a
-wrapper to gettext suite, specifically written for the purpose of Git
-l10n workflow.
+`git-po-helper` is a helper for Git l10n coordinators and contributors. It
+automates checks that contributions follow project conventions (PO syntax,
+commit messages, which paths may change, and related rules) and can work with
+AI coding agents for tasks such as translating new entries, and reviewing
+translations.
 
-To build and install the helper program from source, see
-[git-po-helper/README][].
+Build and install instructions are in [git-po-helper/README][].
 
 
 ## Conventions
diff --git a/po/bg.po b/po/bg.po
index aa96f8b..867678d 100644
--- a/po/bg.po
+++ b/po/bg.po
@@ -16,7 +16,7 @@
 # real merge същинско сливане (а не превъртане)
 # three-way merge тройно сливане
 # octopus merge множествено сливане
-# stale remote старо хранилище
+# stale с изтекъл срок
 # rebase пребазирам
 # merge base база за сливане
 # force (push) принудително изтласквам
@@ -74,6 +74,7 @@
 # mainline базово подаване - при cherry-pick на merge - към коя версия да се изчислява разликата
 # token лексема
 # trailer епилог/завършек на съобщение
+# trailer file файл-епилог
 # cwd текуща работна директория
 # untracked cache кеш за неследените файлове
 # broken/corrupt повреден
@@ -137,6 +138,7 @@
 # island marks граници на групите
 # reflog журнал на указателите
 # hash контролна сума, изчисляване на контролна сума
+# crc контролна сума за циклична проверка
 # fanout откъс за разпределяне
 # idx - index of pack file, 1 index per 1 packfile
 # midx, multi-pack index - файл с индекса за множество пакети
@@ -203,6 +205,7 @@
 # unclean завършвам работа/задача с грешка
 # cache tree кеш за обекти-дървета
 # gitattributes file файл с атрибути на git
+# advertise обявявам
 # advertised обявен за наличен
 # superproject свръхпроект
 # rev-index обратен индекс (reverse index)
@@ -232,7 +235,7 @@
 # init timeout максимално първоначално изчакване
 # implies option включва опцията
 # cache-tree кеша на обектите-дървета
-# acquire lock придобивам ключалка
+# acquire lock заключвам ключалка
 # detached отделѐн, несвързан
 # revision walk обхождане на версиите
 # exit code изходен код
@@ -251,9 +254,18 @@
 # unstage изваждам от индекса
 # upstream branch следен клон
 # revert отменям
-#
-#
-#
+# rewrite презаписвам
+# ancestor предшественик
+# descendent наследник
+# split commit празно подаване
+# committish подаване
+# SHA-1... - SHA1 без тире, подобно на MD5
+# lock (pid) file заключващ файл (с идентификатор на процес)
+# non-native hook name чуждо име на кука
+# abort преустановяване на действието
+# rate limited скоростта е ограничена
+# submodule подмодул
+# credential идентификационни данни
 # ------------------------
 # „$var“ - може да не сработва за shell има gettext и eval_gettext - проверка - намират се лесно по „$
 # ------------------------
@@ -264,6 +276,9 @@
 # FIXME
 # git fetch --al работи подобно на --all
 #
+#
+#
+#
 # ----
 #
 # TODO
@@ -281,21 +296,17 @@
 msgstr ""
 "Project-Id-Version: git v2.53.0-rc0\n"
 "Report-Msgid-Bugs-To: Git Mailing List <git@vger.kernel.org>\n"
-"POT-Creation-Date: 2026-01-23 18:13+0100\n"
-"PO-Revision-Date: 2026-01-24 11:18+0100\n"
+"POT-Creation-Date: 2026-04-19 16:08+0200\n"
+"PO-Revision-Date: 2026-04-19 16:07+0200\n"
 "Last-Translator: Alexander Shopov <ash@kambanaria.org>\n"
 "Language-Team: Bulgarian <dict@fsa-bg.org>\n"
-"Language: bg\\n\"\n"
+"Language: bg\n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
 "Plural-Forms: Plural-Forms: nplurals=2; plural=(n != 1);\n"
 
 #, c-format
-msgid "%s cannot be negative"
-msgstr "%s трябва да е неотрицателно"
-
-#, c-format
 msgid "Huh (%s)?"
 msgstr "Неуспешен анализ — „%s“."
 
@@ -449,20 +460,20 @@
 msgstr "Изход.\n"
 
 #, c-format
-msgid "Stage mode change [y,n,q,a,d%s,?]? "
-msgstr "Добавяне на промяната на права̀та за достъп [y,n,q,a,d%s,?]? "
+msgid "Stage mode change%s [y,n,q,a,d%s,?]? "
+msgstr "Добавяне на промяната на права̀та за достъп%s [y,n,q,a,d%s,?]? "
 
 #, c-format
-msgid "Stage deletion [y,n,q,a,d%s,?]? "
-msgstr "Добавяне на изтриването [y,n,q,a,d%s,?]? "
+msgid "Stage deletion%s [y,n,q,a,d%s,?]? "
+msgstr "Добавяне на изтриването%s [y,n,q,a,d%s,?]? "
 
 #, c-format
-msgid "Stage addition [y,n,q,a,d%s,?]? "
-msgstr "Добавяне на добавянето [y,n,q,a,d%s,?]? "
+msgid "Stage addition%s [y,n,q,a,d%s,?]? "
+msgstr "Добавяне на добавянето%s [y,n,q,a,d%s,?]? "
 
 #, c-format
-msgid "Stage this hunk [y,n,q,a,d%s,?]? "
-msgstr "Добавяне на това парче [y,n,q,a,d%s,?]? "
+msgid "Stage this hunk%s [y,n,q,a,d%s,?]? "
+msgstr "Добавяне на това парче%s [y,n,q,a,d%s,?]? "
 
 msgid ""
 "If the patch applies cleanly, the edited hunk will immediately be marked for "
@@ -485,20 +496,20 @@
 "d — без добавяне на това и всички следващи парчета от файла в индекса\n"
 
 #, c-format
-msgid "Stash mode change [y,n,q,a,d%s,?]? "
-msgstr "Скатаване на промяната на права̀та за достъп [y,n,q,a,d%s,?]? "
+msgid "Stash mode change%s [y,n,q,a,d%s,?]? "
+msgstr "Скатаване на промяната на права̀та за достъп%s [y,n,q,a,d%s,?]? "
 
 #, c-format
-msgid "Stash deletion [y,n,q,a,d%s,?]? "
-msgstr "Скатаване на изтриването [y,n,q,a,d%s,?]? "
+msgid "Stash deletion%s [y,n,q,a,d%s,?]? "
+msgstr "Скатаване на изтриването%s [y,n,q,a,d%s,?]? "
 
 #, c-format
-msgid "Stash addition [y,n,q,a,d%s,?]? "
-msgstr "Скатаване на добавянето [y,n,q,a,d%s,?]? "
+msgid "Stash addition%s [y,n,q,a,d%s,?]? "
+msgstr "Скатаване на добавянето%s [y,n,q,a,d%s,?]? "
 
 #, c-format
-msgid "Stash this hunk [y,n,q,a,d%s,?]? "
-msgstr "Скатаване на това парче [y,n,q,a,d%s,?]? "
+msgid "Stash this hunk%s [y,n,q,a,d%s,?]? "
+msgstr "Скатаване на това парче%s [y,n,q,a,d%s,?]? "
 
 msgid ""
 "If the patch applies cleanly, the edited hunk will immediately be marked for "
@@ -521,20 +532,20 @@
 "d — без скатаване на това и всички следващи парчета от файла\n"
 
 #, c-format
-msgid "Unstage mode change [y,n,q,a,d%s,?]? "
-msgstr "Изваждане на промяната на права̀та за достъп [y,n,q,a,d%s,?]? "
+msgid "Unstage mode change%s [y,n,q,a,d%s,?]? "
+msgstr "Изваждане на промяната на права̀та за достъп%s [y,n,q,a,d%s,?]? "
 
 #, c-format
-msgid "Unstage deletion [y,n,q,a,d%s,?]? "
-msgstr "Изваждане на изтриването [y,n,q,a,d%s,?]? "
+msgid "Unstage deletion%s [y,n,q,a,d%s,?]? "
+msgstr "Изваждане на изтриването%s [y,n,q,a,d%s,?]? "
 
 #, c-format
-msgid "Unstage addition [y,n,q,a,d%s,?]? "
-msgstr "Изваждане на добавянето [y,n,q,a,d%s,?]? "
+msgid "Unstage addition%s [y,n,q,a,d%s,?]? "
+msgstr "Изваждане на добавянето%s [y,n,q,a,d%s,?]? "
 
 #, c-format
-msgid "Unstage this hunk [y,n,q,a,d%s,?]? "
-msgstr "Изваждане на това парче [y,n,q,a,d%s,?]? "
+msgid "Unstage this hunk%s [y,n,q,a,d%s,?]? "
+msgstr "Изваждане на това парче%s [y,n,q,a,d%s,?]? "
 
 msgid ""
 "If the patch applies cleanly, the edited hunk will immediately be marked for "
@@ -557,21 +568,21 @@
 "d — без изваждане на това и всички следващи парчета от файла от индекса\n"
 
 #, c-format
-msgid "Apply mode change to index [y,n,q,a,d%s,?]? "
+msgid "Apply mode change to index%s [y,n,q,a,d%s,?]? "
 msgstr ""
-"Прилагане на промяната на права̀та за достъп към индекса [y,n,q,a,d%s,?]? "
+"Прилагане на промяната на права̀та за достъп към индекса%s [y,n,q,a,d%s,?]? "
 
 #, c-format
-msgid "Apply deletion to index [y,n,q,a,d%s,?]? "
-msgstr "Прилагане на изтриването към индекса [y,n,q,a,d%s,?]? "
+msgid "Apply deletion to index%s [y,n,q,a,d%s,?]? "
+msgstr "Прилагане на изтриването към индекса%s [y,n,q,a,d%s,?]? "
 
 #, c-format
-msgid "Apply addition to index [y,n,q,a,d%s,?]? "
-msgstr "Прилагане на добавянето към индекса [y,n,q,a,d%s,?]? "
+msgid "Apply addition to index%s [y,n,q,a,d%s,?]? "
+msgstr "Прилагане на добавянето към индекса%s [y,n,q,a,d%s,?]? "
 
 #, c-format
-msgid "Apply this hunk to index [y,n,q,a,d%s,?]? "
-msgstr "Прилагане на това парче към индекса [y,n,q,a,d%s,?]? "
+msgid "Apply this hunk to index%s [y,n,q,a,d%s,?]? "
+msgstr "Прилагане на това парче към индекса%s [y,n,q,a,d%s,?]? "
 
 msgid ""
 "If the patch applies cleanly, the edited hunk will immediately be marked for "
@@ -594,22 +605,22 @@
 "d — без прилагане на това и всички следващи парчета от файла към индекса\n"
 
 #, c-format
-msgid "Discard mode change from worktree [y,n,q,a,d%s,?]? "
+msgid "Discard mode change from worktree%s [y,n,q,a,d%s,?]? "
 msgstr ""
-"Премахване на промяната в права̀та за достъп от работното дърво "
+"Премахване на промяната в права̀та за достъп от работното дърво%s "
 "[y,n,q,a,d%s,?]? "
 
 #, c-format
-msgid "Discard deletion from worktree [y,n,q,a,d%s,?]? "
-msgstr "Премахване на изтриването от работното дърво [y,n,q,a,d%s,?]? "
+msgid "Discard deletion from worktree%s [y,n,q,a,d%s,?]? "
+msgstr "Премахване на изтриването от работното дърво%s [y,n,q,a,d%s,?]? "
 
 #, c-format
-msgid "Discard addition from worktree [y,n,q,a,d%s,?]? "
-msgstr "Премахване на добавянето от работното дърво [y,n,q,a,d%s,?]? "
+msgid "Discard addition from worktree%s [y,n,q,a,d%s,?]? "
+msgstr "Премахване на добавянето от работното дърво%s [y,n,q,a,d%s,?]? "
 
 #, c-format
-msgid "Discard this hunk from worktree [y,n,q,a,d%s,?]? "
-msgstr "Премахване на парчето от работното дърво [y,n,q,a,d%s,?]? "
+msgid "Discard this hunk from worktree%s [y,n,q,a,d%s,?]? "
+msgstr "Премахване на парчето от работното дърво%s [y,n,q,a,d%s,?]? "
 
 msgid ""
 "If the patch applies cleanly, the edited hunk will immediately be marked for "
@@ -635,24 +646,24 @@
 "дърво\n"
 
 #, c-format
-msgid "Discard mode change from index and worktree [y,n,q,a,d%s,?]? "
+msgid "Discard mode change from index and worktree%s [y,n,q,a,d%s,?]? "
 msgstr ""
-"Премахване на промяната в права̀та за достъп от индекса и работното дърво "
+"Премахване на промяната в права̀та за достъп от индекса и работното дърво%s "
 "[y,n,q,a,d%s,?]? "
 
 #, c-format
-msgid "Discard deletion from index and worktree [y,n,q,a,d%s,?]? "
+msgid "Discard deletion from index and worktree%s [y,n,q,a,d%s,?]? "
 msgstr ""
-"Премахване на изтриването от индекса и работното дърво [y,n,q,a,d%s,?]? "
+"Премахване на изтриването от индекса и работното дърво%s [y,n,q,a,d%s,?]? "
 
 #, c-format
-msgid "Discard addition from index and worktree [y,n,q,a,d%s,?]? "
+msgid "Discard addition from index and worktree%s [y,n,q,a,d%s,?]? "
 msgstr ""
-"Премахване на добавянето от индекса и работното дърво [y,n,q,a,d%s,?]? "
+"Премахване на добавянето от индекса и работното дърво%s [y,n,q,a,d%s,?]? "
 
 #, c-format
-msgid "Discard this hunk from index and worktree [y,n,q,a,d%s,?]? "
-msgstr "Премахване на парчето от индекса и работното дърво [y,n,q,a,d%s,?]? "
+msgid "Discard this hunk from index and worktree%s [y,n,q,a,d%s,?]? "
+msgstr "Премахване на парчето от индекса и работното дърво%s [y,n,q,a,d%s,?]? "
 
 msgid ""
 "y - discard this hunk from index and worktree\n"
@@ -671,23 +682,24 @@
 "работното дърво\n"
 
 #, c-format
-msgid "Apply mode change to index and worktree [y,n,q,a,d%s,?]? "
+msgid "Apply mode change to index and worktree%s [y,n,q,a,d%s,?]? "
 msgstr ""
-"Прилагане на промяната в права̀та за достъп от индекса и работното дърво "
+"Прилагане на промяната в права̀та за достъп от индекса и работното дърво%s "
 "[y,n,q,a,d%s,?]? "
 
 #, c-format
-msgid "Apply deletion to index and worktree [y,n,q,a,d%s,?]? "
+msgid "Apply deletion to index and worktree%s [y,n,q,a,d%s,?]? "
 msgstr ""
-"Прилагане на изтриването от индекса и работното дърво [y,n,q,a,d%s,?]? "
+"Прилагане на изтриването към индекса и работното дърво%s [y,n,q,a,d%s,?]? "
 
 #, c-format
-msgid "Apply addition to index and worktree [y,n,q,a,d%s,?]? "
-msgstr "Прилагане на добавянето от индекса и работното дърво [y,n,q,a,d%s,?]? "
+msgid "Apply addition to index and worktree%s [y,n,q,a,d%s,?]? "
+msgstr ""
+"Прилагане на добавянето от индекса и работното дърво%s [y,n,q,a,d%s,?]? "
 
 #, c-format
-msgid "Apply this hunk to index and worktree [y,n,q,a,d%s,?]? "
-msgstr "Прилагане на парчето от индекса и работното дърво [y,n,q,a,d%s,?]? "
+msgid "Apply this hunk to index and worktree%s [y,n,q,a,d%s,?]? "
+msgstr "Прилагане на парчето от индекса и работното дърво%s [y,n,q,a,d%s,?]? "
 
 msgid ""
 "y - apply this hunk to index and worktree\n"
@@ -706,22 +718,22 @@
 "работното дърво\n"
 
 #, c-format
-msgid "Apply mode change to worktree [y,n,q,a,d%s,?]? "
+msgid "Apply mode change to worktree%s [y,n,q,a,d%s,?]? "
 msgstr ""
-"Прилагане на промяната в права̀та за достъп към работното дърво "
+"Прилагане на промяната в права̀та за достъп към работното дърво%s "
 "[y,n,q,a,d%s,?]? "
 
 #, c-format
-msgid "Apply deletion to worktree [y,n,q,a,d%s,?]? "
-msgstr "Прилагане на изтриването към работното дърво [y,n,q,a,d%s,?]? "
+msgid "Apply deletion to worktree%s [y,n,q,a,d%s,?]? "
+msgstr "Прилагане на изтриването към работното дърво%s [y,n,q,a,d%s,?]? "
 
 #, c-format
-msgid "Apply addition to worktree [y,n,q,a,d%s,?]? "
-msgstr "Прилагане на добавянето към работното дърво [y,n,q,a,d%s,?]? "
+msgid "Apply addition to worktree%s [y,n,q,a,d%s,?]? "
+msgstr "Прилагане на добавянето към работното дърво%s [y,n,q,a,d%s,?]? "
 
 #, c-format
-msgid "Apply this hunk to worktree [y,n,q,a,d%s,?]? "
-msgstr "Прилагане на парчето към работното дърво [y,n,q,a,d%s,?]? "
+msgid "Apply this hunk to worktree%s [y,n,q,a,d%s,?]? "
+msgstr "Прилагане на парчето към работното дърво%s [y,n,q,a,d%s,?]? "
 
 msgid ""
 "y - apply this hunk to worktree\n"
@@ -737,6 +749,10 @@
 "d — без прилагане на това и всички следващи парчета от файла\n"
 
 #, c-format
+msgid "%s cannot be negative"
+msgstr "%s трябва да е неотрицателно"
+
+#, c-format
 msgid "could not parse hunk header '%.*s'"
 msgstr "заглавната част на парчето „%.*s“ не може да се анализира"
 
@@ -803,7 +819,7 @@
 msgstr ""
 "Ако е невъзможно чисто прилагане на кода, ще може пак да редактирате.  Ако\n"
 "изтриете всички редове от парчето код, то ще се остави непроменено, а\n"
-"редактирането — отказано.\n"
+"редактирането — преустановено.\n"
 
 msgid "could not parse hunk header"
 msgstr "заглавната част парчето не може да се анализира"
@@ -833,6 +849,7 @@
 msgid "Nothing was applied.\n"
 msgstr "Нищо не е приложено.\n"
 
+#, c-format
 msgid ""
 "j - go to the next undecided hunk, roll over at the bottom\n"
 "J - go to the next hunk, roll over at the bottom\n"
@@ -844,26 +861,45 @@
 "e - manually edit the current hunk\n"
 "p - print the current hunk\n"
 "P - print the current hunk using the pager\n"
+"> - go to the next file, roll over at the bottom\n"
+"< - go to the previous file, roll over at the top\n"
 "? - print help\n"
+"HUNKS SUMMARY - Hunks: %d, USE: %d, SKIP: %d\n"
 msgstr ""
-"j — без решение за парчето, към следващото парче без решение, превъртане "
-"след края\n"
-"J — без решение за парчето, към следващото парче, превъртане след края\n"
-"k — без решение за парчето, към предишното парче без решение, превъртане "
-"след началото\n"
-"K — без решение за парчето, към предишното парче, превъртане след началото\n"
+"j — към следващото парче без решение, превъртане след края\n"
+"J — към следващото парче, превъртане след края\n"
+"k — към предишното парче без решение, превъртане след началото\n"
+"K — към предишното парче, превъртане след началото\n"
 "g — избор към кое парче да се премине\n"
 "/ — търсене на парче, напасващо към даден регулярен израз\n"
 "s — разделяне на текущото парче на по-малки\n"
 "e — ръчно редактиране на текущото парче\n"
 "p — извеждане на текущото парче\n"
 "P — извеждане на текущото парче през програма за прелистване\n"
-"? — извеждане на помощта\n"
+"> — към следващия файл, превъртане след края\n"
+"< — към предишния файл, превъртане след началото\n"
+"? — извеждане на помощта<\n"
+"ОБОБЩЕНИЕ ЗА ПАРЧЕТАТА — парчета: %d, ПОЛЗВАНИ: %d, ПРОПУСНАТИ: %d\n"
+
+msgid "'git apply' failed"
+msgstr "неуспешно изпълнение на „git apply“"
+
+msgid " (was: y)"
+msgstr " (бе: „y“)"
+
+msgid " (was: n)"
+msgstr " (бе: „n“)"
 
 #, c-format
 msgid "Only one letter is expected, got '%s'"
 msgstr "Очаква се само един знак, а не „%s“"
 
+msgid "No next file"
+msgstr "Няма следващ файл"
+
+msgid "No previous file"
+msgstr "Няма предишен файл"
+
 msgid "No other hunk"
 msgstr "Няма друго парче"
 
@@ -903,7 +939,7 @@
 msgstr "Никое парче не напасва на регулярния израз"
 
 msgid "Sorry, cannot split this hunk"
-msgstr "Това парче не може да се раздели"
+msgstr "Това парче не може да се разделѝ"
 
 #, c-format
 msgid "Split into %d hunks."
@@ -916,9 +952,6 @@
 msgid "Unknown command '%s' (use '?' for help)"
 msgstr "Непозната команда „%s“ (за помощ натиснете „?“)"
 
-msgid "'git apply' failed"
-msgstr "неуспешно изпълнение на „git apply“"
-
 msgid "No changes."
 msgstr "Няма промѐни."
 
@@ -926,6 +959,25 @@
 msgstr "Променени са само двоични файлове."
 
 #, c-format
+msgid "Stage mode change [y,n,q,a,d%s,?]? "
+msgstr "Добавяне на промяната на права̀та за достъп [y,n,q,a,d%s,?]? "
+
+#, c-format
+msgid "Stage deletion [y,n,q,a,d%s,?]? "
+msgstr "Добавяне на изтриването [y,n,q,a,d%s,?]? "
+
+#, c-format
+msgid "Stage addition [y,n,q,a,d%s,?]? "
+msgstr "Добавяне на добавянето [y,n,q,a,d%s,?]? "
+
+#, c-format
+msgid "Stage this hunk [y,n,q,a,d%s,?]? "
+msgstr "Добавяне на това парче [y,n,q,a,d%s,?]? "
+
+msgid "Revision does not refer to a commit"
+msgstr "Версията не сочи към подаване"
+
+#, c-format
 msgid ""
 "\n"
 "Disable this message with \"git config set advice.%s false\""
@@ -985,7 +1037,7 @@
 "\n"
 "\tgit rebase\n"
 msgstr ""
-"Раздалечили се клони не може да се превъртят.  Ползвайте:\n"
+"Раздалечили се кло̀ни не може да се превъртят.  Ползвайте:\n"
 "\n"
 "    git merge --no-ff\n"
 "\n"
@@ -1118,8 +1170,14 @@
 msgstr "Регулярният израз върна %d при подадена последователност „%s“ на входа"
 
 #, c-format
-msgid "unable to find filename in patch at line %d"
-msgstr "Липсва име на файл на ред %d от кръпката"
+msgid "unable to find filename in patch at %s:%d"
+msgstr "в кръпката липсва име на файл при %s:%d"
+
+#, c-format
+msgid "git apply: bad git-diff - expected /dev/null, got %s at %s:%d"
+msgstr ""
+"git apply: лош изход от командата „git-diff“ — на ред %2$s:%3$d се очакваше "
+"„/dev/null“, а бе получен „%1$s“"
 
 #, c-format
 msgid "git apply: bad git-diff - expected /dev/null, got %s on line %d"
@@ -1128,6 +1186,18 @@
 "null“, а бе получен „%1$s“"
 
 #, c-format
+msgid "git apply: bad git-diff - inconsistent new filename at %s:%d"
+msgstr ""
+"git apply: лош изход от командата „git-diff“ — на ред %s:%d бе получено "
+"неправилно име на нов файл"
+
+#, c-format
+msgid "git apply: bad git-diff - inconsistent old filename at %s:%d"
+msgstr ""
+"git apply: лош изход от командата „git-diff“ — на ред %s:%d бе получено "
+"неправилно име на стар файл"
+
+#, c-format
 msgid "git apply: bad git-diff - inconsistent new filename on line %d"
 msgstr ""
 "git apply: лош изход от командата „git-diff“ — на ред %d бе получено "
@@ -1140,14 +1210,24 @@
 "неправилно име на стар файл"
 
 #, c-format
+msgid "git apply: bad git-diff - expected /dev/null at %s:%d"
+msgstr ""
+"git apply: лош изход от командата „git-diff“ — на ред %s:%d се очакваше „/"
+"dev/null“"
+
+#, c-format
 msgid "git apply: bad git-diff - expected /dev/null on line %d"
 msgstr ""
 "git apply: лош изход от командата „git-diff“ — на ред %d се очакваше „/dev/"
 "null“"
 
 #, c-format
+msgid "invalid mode at %s:%d: %s"
+msgstr "грешен режим на ред %s:%d: %s"
+
+#, c-format
 msgid "invalid mode on line %d: %s"
-msgstr "грешен режим на ред №%d: %s"
+msgstr "грешен режим на ред %d: %s"
 
 #, c-format
 msgid "inconsistent header lines %d and %d"
@@ -1156,6 +1236,20 @@
 #, c-format
 msgid ""
 "git diff header lacks filename information when removing %d leading pathname "
+"component at %s:%d"
+msgid_plural ""
+"git diff header lacks filename information when removing %d leading pathname "
+"components at %s:%d"
+msgstr[0] ""
+"След съкращаването на %d-та част от компонентите на пътя, в заглавната част "
+"на „git diff“ липсва информация за име на файл (ред: %s:%d)"
+msgstr[1] ""
+"След съкращаването на първите %d части от компонентите на пътя, в заглавната "
+"част на „git diff“ липсва информация за име на файл (ред: %s:%d)"
+
+#, c-format
+msgid ""
+"git diff header lacks filename information when removing %d leading pathname "
 "component (line %d)"
 msgid_plural ""
 "git diff header lacks filename information when removing %d leading pathname "
@@ -1168,6 +1262,11 @@
 "част на „git diff“ липсва информация за име на файл (ред: %d)"
 
 #, c-format
+msgid "git diff header lacks filename information at %s:%d"
+msgstr ""
+"в заглавната част на „git diff“ липсва информация за име на файл на ред %s:%d"
+
+#, c-format
 msgid "git diff header lacks filename information (line %d)"
 msgstr ""
 "в заглавната част на „git diff“ липсва информация за име на файл (ред: %d)"
@@ -1177,8 +1276,8 @@
 msgstr "при повторното преброяване бе получен неочакван ред: „%.*s“"
 
 #, c-format
-msgid "patch fragment without header at line %d: %.*s"
-msgstr "част от кръпка без заглавна част на ред %d: %.*s"
+msgid "patch fragment without header at %s:%d: %.*s"
+msgstr "част от кръпка без заглавна част на ред %s:%d: %.*s"
 
 msgid "new file depends on old contents"
 msgstr "новият файл зависи от старото съдържание на файла"
@@ -1187,8 +1286,8 @@
 msgstr "изтритият файл не е празен"
 
 #, c-format
-msgid "corrupt patch at line %d"
-msgstr "грешка в кръпката на ред %d"
+msgid "corrupt patch at %s:%d"
+msgstr "грешка в кръпката на ред %s:%d"
 
 #, c-format
 msgid "new file %s depends on old contents"
@@ -1203,16 +1302,16 @@
 msgstr "● предупреждение: файлът „%s“ вече е празен, но не е изтрит"
 
 #, c-format
-msgid "corrupt binary patch at line %d: %.*s"
-msgstr "грешка в двоичната кръпка на ред %d: %.*s"
+msgid "corrupt binary patch at %s:%d: %.*s"
+msgstr "грешка в двоичната кръпка на ред %s:%d: %.*s"
 
 #, c-format
-msgid "unrecognized binary patch at line %d"
-msgstr "неразпозната двоичната кръпка на ред %d"
+msgid "unrecognized binary patch at %s:%d"
+msgstr "неразпозната двоичната кръпка на ред %s:%d"
 
 #, c-format
-msgid "patch with only garbage at line %d"
-msgstr "кръпката е с изцяло повредени данни на ред %d"
+msgid "patch with only garbage at %s:%d"
+msgstr "кръпката е с изцяло повредени данни на ред %s:%d"
 
 #, c-format
 msgid "unable to read symlink %s"
@@ -1411,7 +1510,7 @@
 
 #, c-format
 msgid "corrupt patch for submodule %s"
-msgstr "повредена кръпка за модула „%s“"
+msgstr "повредена кръпка за подмодула „%s“"
 
 #, c-format
 msgid "unable to stat newly created file '%s'"
@@ -1480,6 +1579,14 @@
 msgstr "индексът не може да се запише"
 
 #, c-format
+msgid "option -p expects a non-negative integer, got '%s'"
+msgstr "опцията „-p“ изисква неотрицателно цяло число, а не зададеното: „%s“"
+
+#, c-format
+msgid "unable to normalize directory: '%s'"
+msgstr "името на директорията „%s“ не може да се приведе в нормален вид"
+
+#, c-format
 msgid "can't open patch '%s': %s"
 msgstr "кръпката „%s“ не може да се отвори: %s"
 
@@ -2052,7 +2159,7 @@
 "%s\n"
 "Най-често това е грешка в настройките.\n"
 "\n"
-"За конфигурирането на следящи клони трябва указателите за изтегляне да "
+"За конфигурирането на следящи кло̀ни трябва указателите за изтегляне да "
 "съответстват\n"
 "на различни пространства от имена."
 
@@ -2179,6 +2286,11 @@
 msgid "select hunks interactively"
 msgstr "интерактивен избор на парчета код"
 
+msgid "auto advance to the next file when selecting hunks interactively"
+msgstr ""
+"автоматично преминаване към следващия файл при интерактивно избиране на "
+"парчета код"
+
 msgid "edit current diff and apply"
 msgstr "редактиране на текущата разлика и прилагане"
 
@@ -2641,7 +2753,7 @@
 "it will be removed. Please do not use it anymore."
 msgstr ""
 "Опциите „-b“/„--binary“ отдавна не правят нищо и\n"
-"ще бъдат премахнати в бъдеще.  Не ги ползвайте."
+"ще се премахнат в бъдеще.  Не ги ползвайте."
 
 msgid "failed to read the index"
 msgstr "неуспешно изчитане на индекса"
@@ -2698,6 +2810,10 @@
 msgid "Restrict the missing objects to the current sparse-checkout"
 msgstr "Ограничаване на липсващите обекти до текущото частично хранилище"
 
+#, c-format
+msgid "unrecognized argument: %s"
+msgstr "непознат аргумент: %s"
+
 msgid ""
 "git bisect start [--term-(bad|new)=<term-new> --term-(good|old)=<term-old>]\n"
 "                 [--no-checkout] [--first-parent] [<bad> [<good>...]] [--] "
@@ -3194,7 +3310,7 @@
 
 #, c-format
 msgid "couldn't look up commit object for '%s'"
-msgstr "обектът-подаване за „%s“ не може да се открие"
+msgstr "обектът-подаване за „%s“ липсва"
 
 #, c-format
 msgid "the branch '%s' is not fully merged"
@@ -3308,7 +3424,7 @@
 msgstr ""
 "Въведете описание на клона.\n"
 "    %s\n"
-"Редовете, които започват с „%s“, ще бъдат пропуснати.\n"
+"Редовете, които започват с „%s“, ще се пропуснат.\n"
 
 msgid "Generic options"
 msgstr "Общи настройки"
@@ -3316,7 +3432,7 @@
 msgid "show hash and subject, give twice for upstream branch"
 msgstr ""
 "извеждане на контролната сума и темата.  Повтарянето на опцията прибавя "
-"отдалечените клони"
+"отдалечените кло̀ни"
 
 msgid "suppress informational messages"
 msgstr "без информационни съобщения"
@@ -3340,25 +3456,25 @@
 msgstr "цветен изход"
 
 msgid "act on remote-tracking branches"
-msgstr "действие върху следящите клони"
+msgstr "действие върху следящите кло̀ни"
 
 msgid "print only branches that contain the commit"
-msgstr "извеждане само на клоните, които съдържат това ПОДАВАНЕ"
+msgstr "извеждане само на кло̀ните, които съдържат това ПОДАВАНЕ"
 
 msgid "print only branches that don't contain the commit"
-msgstr "извеждане само на клоните, които не съдържат това ПОДАВАНЕ"
+msgstr "извеждане само на кло̀ните, които не съдържат това ПОДАВАНЕ"
 
 msgid "Specific git-branch actions:"
 msgstr "Специални действия на „git-branch“:"
 
 msgid "list both remote-tracking and local branches"
-msgstr "извеждане както на следящите, така и на локалните клони"
+msgstr "извеждане както на следящите, така и на локалните кло̀ни"
 
 msgid "delete fully merged branch"
-msgstr "изтриване на клони, които са напълно слети"
+msgstr "изтриване на кло̀ни, които са напълно слети"
 
 msgid "delete branch (even if not merged)"
-msgstr "изтриване и на клони, които не са напълно слети"
+msgstr "изтриване и на кло̀ни, които не са напълно слети"
 
 msgid "move/rename a branch and its reflog"
 msgstr ""
@@ -3377,7 +3493,7 @@
 msgstr "копиране на клон, дори ако има вече клон с такова име"
 
 msgid "list branch names"
-msgstr "извеждане на имената на клоните"
+msgstr "извеждане на имената на кло̀ните"
 
 msgid "show current branch name"
 msgstr "извеждане на името на текущия клон"
@@ -3392,10 +3508,10 @@
 msgstr "принудително създаване, преместване, преименуване, изтриване"
 
 msgid "print only branches that are merged"
-msgstr "извеждане само на слетите клони"
+msgstr "извеждане само на слетите кло̀ни"
 
 msgid "print only branches that are not merged"
-msgstr "извеждане само на неслетите клони"
+msgstr "извеждане само на неслетите кло̀ни"
 
 msgid "list branches in columns"
 msgstr "извеждане по колони"
@@ -3404,7 +3520,7 @@
 msgstr "ОБЕКТ"
 
 msgid "print only branches of the object"
-msgstr "извеждане само на клоните на ОБЕКТА"
+msgstr "извеждане само на кло̀ните на ОБЕКТА"
 
 msgid "sorting and filtering are case insensitive"
 msgstr "подредбата и филтрирането третират еднакво малките и главните букви"
@@ -3430,7 +3546,7 @@
 
 msgid "--recurse-submodules can only be used to create branches"
 msgstr ""
-"опцията „--recurse-submodules“ може да се ползва само за създаването на клони"
+"опцията „--recurse-submodules“ може да се ползва само за създаването на кло̀ни"
 
 msgid "branch name required"
 msgstr "Необходимо е име на клон"
@@ -3449,7 +3565,7 @@
 "не може да преименувате текущия клон, защото сте извън който и да е клон"
 
 msgid "too many branches for a copy operation"
-msgstr "прекалено много клони за копиране"
+msgstr "прекалено много кло̀ни за копиране"
 
 msgid "too many arguments for a rename operation"
 msgstr "прекалено много аргументи към командата за преименуване"
@@ -3573,7 +3689,7 @@
 
 #, c-format
 msgid "could not create leading directories for '%s'"
-msgstr "родителските директории на „%s“ не може да бъдат създадени"
+msgstr "родителските директории на „%s“ не може да се създадат"
 
 #, c-format
 msgid "unable to create diagnostics archive %s"
@@ -3934,18 +4050,6 @@
 msgid "copy out the files from named stage"
 msgstr "копиране на файловете от това състояние на сливане"
 
-msgid "git checkout [<options>] <branch>"
-msgstr "git checkout [ОПЦИЯ…] КЛОН"
-
-msgid "git checkout [<options>] [<branch>] -- <file>..."
-msgstr "git checkout [ОПЦИЯ…] [КЛОН] -- ФАЙЛ…"
-
-msgid "git switch [<options>] [<branch>]"
-msgstr "git switch [ОПЦИЯ…] КЛОН"
-
-msgid "git restore [<options>] [--source=<branch>] <file>..."
-msgstr "git restore [ОПЦИЯ…] [--source=КЛОН] ФАЙЛ…"
-
 #, c-format
 msgid "path '%s' does not have our version"
 msgstr "вашата версия липсва в пътя „%s“"
@@ -4131,11 +4235,12 @@
 "„%s“ може да е както локален файл, така и следящ клон.  За уточняване\n"
 "ползвайте разделителя „--“ (и евентуално опцията „--no-guess“)"
 
+#, c-format
 msgid ""
 "If you meant to check out a remote tracking branch on, e.g. 'origin',\n"
 "you can do so by fully qualifying the name with the --track option:\n"
 "\n"
-"    git checkout --track origin/<name>\n"
+"    git %s --track origin/<name>\n"
 "\n"
 "If you'd like to always have checkouts of an ambiguous <name> prefer\n"
 "one remote, e.g. the 'origin' remote, consider setting\n"
@@ -4144,7 +4249,7 @@
 "Ако искате да изтеглите клона от конкретно хранилище, напр. „origin“,\n"
 "изрично го укажете към опцията „--track“:\n"
 "\n"
-"    git checkout --track origin/ИМЕ_НА_КЛОН\n"
+"    git %s --track origin/ИМЕ_НА_КЛОН\n"
 "\n"
 "Ако искате винаги да се предпочита конкретно хранилище при нееднозначно\n"
 "ИМЕ_НА_КЛОН, напр. „origin“, задайте следната настройка в конфигурационния\n"
@@ -4154,7 +4259,7 @@
 
 #, c-format
 msgid "'%s' matched multiple (%d) remote tracking branches"
-msgstr "„%s“ напасва с множество (%d) отдалечени клони"
+msgstr "„%s“ напасва с множество (%d) отдалечени кло̀ни"
 
 msgid "only one reference expected"
 msgstr "очаква се само един указател"
@@ -4279,7 +4384,7 @@
 msgstr "отделяне на указателя „HEAD“ при указаното подаване"
 
 msgid "force checkout (throw away local modifications)"
-msgstr "принудително изтегляне (вашите промѐни ще бъдат занулени)"
+msgstr "принудително изтегляне (вашите промѐни ще се занулят)"
 
 msgid "new-branch"
 msgstr "НОВ_КЛОН"
@@ -4302,6 +4407,18 @@
 msgid "do not limit pathspecs to sparse entries only"
 msgstr "без ограничаване на шаблоните за пътища само до частично изтеглените"
 
+msgid "git checkout [<options>] <branch>"
+msgstr "git checkout [ОПЦИЯ…] КЛОН"
+
+msgid "git checkout [<options>] [<branch>] -- <file>..."
+msgstr "git checkout [ОПЦИЯ…] [КЛОН] -- ФАЙЛ…"
+
+msgid "git switch [<options>] [<branch>]"
+msgstr "git switch [ОПЦИЯ…] КЛОН"
+
+msgid "git restore [<options>] [--source=<branch>] <file>..."
+msgstr "git restore [ОПЦИЯ…] [--source=КЛОН] ФАЙЛ…"
+
 #, c-format
 msgid "options '-%c', '-%c', and '%s' cannot be used together"
 msgstr "опциите „-%c“, „-%c“ и „%s“ са несъвместими"
@@ -4502,7 +4619,7 @@
 msgstr "Файловете за изчистване свършиха.  Изход от програмата."
 
 msgid "do not print names of files removed"
-msgstr "без извеждане на имената на файловете, които ще бъдат изтрити"
+msgstr "без извеждане на имената на успешно изтритите файлове"
 
 msgid "force"
 msgstr "принудително изтриване"
@@ -4511,7 +4628,7 @@
 msgstr "интерактивно изтриване"
 
 msgid "remove whole directories"
-msgstr "изтриване на цели директории"
+msgstr "изтриване на цѐли директории"
 
 msgid "pattern"
 msgstr "ШАБЛОН"
@@ -4615,7 +4732,7 @@
 msgstr "работното дърво не може да се подготви"
 
 msgid "unable to write parameters to config file"
-msgstr "настройките не може да бъдат записани в конфигурационния файл"
+msgstr "настройките не може да се запишат в конфигурационния файл"
 
 msgid "cannot repack to clean up"
 msgstr "не може да се извърши пакетиране за изчистване на файловете"
@@ -4670,7 +4787,7 @@
 msgstr "ИМЕ"
 
 msgid "use <name> instead of 'origin' to track upstream"
-msgstr "използване на това ИМЕ вместо „origin“ при проследяване на клони"
+msgstr "използване на това ИМЕ вместо „origin“ при проследяване на кло̀ни"
 
 msgid "checkout <branch> instead of the remote's HEAD"
 msgstr "изтегляне на този КЛОН, а не соченият от отдалечения указател „HEAD“"
@@ -4732,7 +4849,7 @@
 msgstr "прилагане на филтрите за непълно хранилище към подмодулите"
 
 msgid "any cloned submodules will use their remote-tracking branch"
-msgstr "всички клонирани подмодули ще ползват следящите си клони"
+msgstr "всички клонирани подмодули ще ползват следящите си кло̀ни"
 
 msgid "initialize sparse-checkout file to include only files at root"
 msgstr ""
@@ -4781,7 +4898,7 @@
 
 #, c-format
 msgid "could not create leading directories of '%s'"
-msgstr "родителските директории на „%s“ не може да бъдат създадени"
+msgstr "родителските директории на „%s“ не може да се създадат"
 
 #, c-format
 msgid "could not create work tree dir '%s'"
@@ -5217,7 +5334,7 @@
 "with '%s' will be ignored.\n"
 msgstr ""
 "Въведете съобщението за подаване на промѐните.  Редовете, които започват\n"
-"с „%s“, ще бъдат пропуснати.\n"
+"с „%s“, ще се пропуснат.\n"
 
 #, c-format
 msgid ""
@@ -5225,15 +5342,15 @@
 "with '%s' will be ignored, and an empty message aborts the commit.\n"
 msgstr ""
 "Въведете съобщението за подаване на промѐните.  Редовете, които започват\n"
-"с „%s“, ще бъдат пропуснати, а празно съобщение преустановява подаването.\n"
+"с „%s“, ще се пропуснат, а празно съобщение преустановява подаването.\n"
 
 #, c-format
 msgid ""
 "Please enter the commit message for your changes. Lines starting\n"
 "with '%s' will be kept; you may remove them yourself if you want to.\n"
 msgstr ""
-"Въведете съобщението за подаване на промѐните.  Редовете, които започват\n"
-"с „%s“, също ще бъдат включени — може да ги изтриете вие.\n"
+"Въведете съобщението за подаване на промѐните.  Редовете, които\n"
+"започват с „%s“, също ще се включат — може да ги изтриете.\n"
 
 #, c-format
 msgid ""
@@ -5241,9 +5358,9 @@
 "with '%s' will be kept; you may remove them yourself if you want to.\n"
 "An empty message aborts the commit.\n"
 msgstr ""
-"Въведете съобщението за подаване на промѐните.  Редовете, които започват\n"
-"с „%s“, също ще бъдат включени — може да ги изтриете вие.  Празно \n"
-"съобщение преустановява подаването.\n"
+"Въведете съобщението за подаване на промѐните.  Редовете, които\n"
+"започват с „%s“, също ще се включат — може да ги изтриете.\n"
+"Празно съобщение преустановява подаването.\n"
 
 msgid ""
 "\n"
@@ -5358,7 +5475,7 @@
 msgstr "кратка информация за състоянието"
 
 msgid "show branch information"
-msgstr "информация за клоните"
+msgstr "информация за кло̀ните"
 
 msgid "show stash information"
 msgstr "информация за скатаното"
@@ -5541,15 +5658,15 @@
 
 #, c-format
 msgid "Aborting commit due to empty commit message.\n"
-msgstr "Неизвършване на подаване поради празно съобщение.\n"
+msgstr "Преустановяване на подаване поради празно съобщение.\n"
 
 #, c-format
 msgid "Aborting commit; you did not edit the message.\n"
-msgstr "Неизвършване на подаване поради нередактирано съобщение.\n"
+msgstr "Преустановяване на подаване поради нередактирано съобщение.\n"
 
 #, c-format
 msgid "Aborting commit due to empty commit message body.\n"
-msgstr "Неизвършване на подаване поради празно съобщение.\n"
+msgstr "Преустановяване на подаване поради празно съобщение.\n"
 
 msgid ""
 "repository has been updated, but unable to write\n"
@@ -5953,7 +6070,7 @@
 "cannot overwrite multiple values with a single value\n"
 "       Use a regexp, --add or --replace-all to change %s."
 msgstr ""
-"множество стойности не може да се замени с една.\n"
+"множество стойности не може да се заменѝ с една.\n"
 "За да променѝте „%s“, ползвайте регулярен израз или опциите „--add“ и „--"
 "replace-all“."
 
@@ -5969,8 +6086,9 @@
 msgstr ""
 "Права̀та за достъп до директорията за програмните гнезда са прекалено "
 "свободни —\n"
-"другите потребители може да получат достъп до кешираните ви пароли.  За да\n"
-"коригирате това, изпълнете:\n"
+"другите потребители може да получат достъп до кешираните ви "
+"идентификационни\n"
+"данни.  За да коригирате това, изпълнете:\n"
 "\n"
 "    chmod 0700 %s"
 
@@ -5979,18 +6097,19 @@
 
 msgid "credential-cache--daemon unavailable; no unix socket support"
 msgstr ""
-"демонът за кеша с идентификациите е недостъпен (credential-cache--daemon) — "
-"липсва поддръжка на гнезда на unix"
+"демонът за кеша с идентификационните данни е недостъпен (credential-cache--"
+"daemon) — липсва поддръжка на гнезда на unix"
 
 msgid "credential-cache unavailable; no unix socket support"
 msgstr ""
-"кешът с идентификациите е недостъпен — липсва поддръжка на гнезда на unix"
+"кешът с идентификационните данни е недостъпен — липсва поддръжка на гнезда "
+"на unix"
 
 #, c-format
 msgid "unable to get credential storage lock in %d ms"
 msgstr ""
-"ключалката на хранилището на идентификациите не бе получена в рамките на %d "
-"ms"
+"ключалката на хранилището на идентификационните данни не бе заключена в "
+"рамките на %d ms"
 
 msgid ""
 "git describe [--all] [--tags] [--contains] [--abbrev=<n>] [<commit-ish>...]"
@@ -6065,7 +6184,7 @@
 
 #, c-format
 msgid "blob '%s' not reachable from HEAD"
-msgstr "обектът-BLOB „%s“ е недостежим от HEAD"
+msgstr "обектът-BLOB „%s“ е недостижим от HEAD"
 
 #, c-format
 msgid "describe %s\n"
@@ -6158,10 +6277,6 @@
 msgid "git diff-pairs -z [<diff-options>]"
 msgstr "git diff-pairs -z [ОПЦИЯ_ЗА_РАЗЛИКИ…]"
 
-#, c-format
-msgid "unrecognized argument: %s"
-msgstr "непознат аргумент: %s"
-
 msgid "working without -z is not supported"
 msgstr "опцията „-z“ е задължителна"
 
@@ -6385,13 +6500,6 @@
 "получено е подписано подаване „%s“ — ползвайте „--signed-commits=РЕЖИМ“ за "
 "обработка"
 
-msgid ""
-"'strip-if-invalid' is not a valid mode for git fast-export with --signed-"
-"commits=<mode>"
-msgstr ""
-"„strip-if-invalid“ е грешен РЕЖИМ за „git fast-export“ с опцията „--signed-"
-"commits=РЕЖИМ“"
-
 #, c-format
 msgid ""
 "omitting tag %s,\n"
@@ -6418,13 +6526,6 @@
 msgstr ""
 "получен е подписан етикет „%s“ — ползвайте „--signed-tags=РЕЖИМ“ за обработка"
 
-msgid ""
-"'strip-if-invalid' is not a valid mode for git fast-export with --signed-"
-"tags=<mode>"
-msgstr ""
-"„strip-if-invalid“ е грешен РЕЖИМ за „git fast-export“ с опцията „--signed-"
-"tags=РЕЖИМ“"
-
 #, c-format
 msgid ""
 "tag %s tags unexported object; use --tag-of-filtered-object=<mode> to handle "
@@ -6628,7 +6729,7 @@
 
 #, c-format
 msgid "unable to create leading directories of %s"
-msgstr "родителските директории на „%s“ не може да бъдат създадени"
+msgstr "родителските директории на „%s“ не може да се създадат"
 
 #, c-format
 msgid "unable to write marks file %s"
@@ -6844,6 +6945,39 @@
 "пропускане на грешен подпис към подаване,\n"
 "  вероятно е на %s"
 
+#, c-format
+msgid ""
+"replacing invalid signature for commit '%.100s...'\n"
+"  allegedly by %s"
+msgstr ""
+"замяна на грешния подпис към подаването „%.100s…“,\n"
+"  вероятно е на %s"
+
+#, c-format
+msgid ""
+"replacing invalid signature for commit '%.*s'\n"
+"  allegedly by %s"
+msgstr ""
+"замяна на грешния подпис към подаването „%.*s“,\n"
+"  вероятно е на %s"
+
+#, c-format
+msgid ""
+"replacing invalid signature for commit\n"
+"  allegedly by %s"
+msgstr ""
+"замяна на грешен подпис към подаване,\n"
+"  вероятно е на %s"
+
+msgid "aborting due to invalid signature"
+msgstr "преустановяване поради неправилен подпис"
+
+msgid "signing commits in interoperability mode is unsupported"
+msgstr "подписването на подавания в режим на съвместимост не се поддържа"
+
+msgid "failed to sign commit object"
+msgstr "обектът за подаването не може да се подпише"
+
 msgid "expected committer but didn't get one"
 msgstr "очакваше се подаващ, но такъв липсва"
 
@@ -6858,6 +6992,9 @@
 msgid "importing a commit signature verbatim"
 msgstr "дословно внасяне на подпис на подаване"
 
+msgid "failed to sign tag object"
+msgstr "обектът за етикет не може да се подпише"
+
 #, c-format
 msgid "importing a tag signature verbatim for tag '%s'"
 msgstr "дословно внасяне на подпис на етикет за „%s“"
@@ -6870,13 +7007,6 @@
 msgstr ""
 "получен е подписан етикет — ползвайте „--signed-tags=РЕЖИМ“ за обработка"
 
-msgid ""
-"'strip-if-invalid' is not a valid mode for git fast-import with --signed-"
-"tags=<mode>"
-msgstr ""
-"„strip-if-invalid“ е грешен РЕЖИМ за „git fast-import“ с опцията „--signed-"
-"tags=РЕЖИМ“"
-
 #, c-format
 msgid "expected 'from' command, got '%s'"
 msgstr "очаква се команда „from“, а бе получена: „%s“"
@@ -7098,7 +7228,7 @@
 "but that check has been disabled; to re-enable, use '--show-forced-updates'\n"
 "flag or run 'git config fetch.showForcedUpdates true'"
 msgstr ""
-"Обичайно при доставяне се извежда, кои клони са били принудително обновени,\n"
+"Обичайно при доставяне се извежда, кои кло̀ни са били принудително обновени,\n"
 "но тази проверка е изключена.  За да я включите за командата, ползвайте "
 "опцията\n"
 "„--show-forced-updates“, а за да я включите за постоянно, изпълнете:\n"
@@ -7124,8 +7254,7 @@
 
 #, c-format
 msgid "rejected %s because shallow roots are not allowed to be updated"
-msgstr ""
-"отхвърляне на „%s“, защото плитките върхове не може да бъдат обновявани"
+msgstr "отхвърляне на „%s“, защото плитките върхове не може да се обновят"
 
 msgid "[deleted]"
 msgstr "[изтрит]"
@@ -7212,8 +7341,8 @@
 "some local refs could not be updated; try running\n"
 " 'git remote prune %s' to remove any old, conflicting branches"
 msgstr ""
-"някои локални указатели не може да бъдат обновени.  Изпълнете командата\n"
-"„git remote prune %s“, за да премахнете остарелите клони, които\n"
+"някои локални указатели не може да се обновят.  Изпълнете командата\n"
+"„git remote prune %s“, за да премахнете остарелите кло̀ни, които\n"
 "предизвикват конфликта"
 
 #, c-format
@@ -7222,7 +7351,7 @@
 
 msgid "multiple branches detected, incompatible with --set-upstream"
 msgstr ""
-"засечени са множество клони, това е несъвместимо с опцията „--set-upstream“"
+"засечени са множество кло̀ни, това е несъвместимо с опцията „--set-upstream“"
 
 #, c-format
 msgid ""
@@ -7306,7 +7435,7 @@
 "prefetch/“"
 
 msgid "prune remote-tracking branches no longer on remote"
-msgstr "окастряне на клоните следящи вече несъществуващи отдалечени клони"
+msgstr "окастряне на кло̀ните следящи вече несъществуващи отдалечени кло̀ни"
 
 msgid "prune local tags no longer on remote and clobber changed tags"
 msgstr ""
@@ -7374,7 +7503,7 @@
 msgstr "изпълняване на „maintenance --auto“ след доставяне"
 
 msgid "check for forced-updates on all updated branches"
-msgstr "проверка за принудителни обновявания на всички клони"
+msgstr "проверка за принудителни обновявания на всички кло̀ни"
 
 msgid "write the commit-graph after fetching"
 msgstr "запазване на гра̀фа с подаванията след доставяне"
@@ -7695,7 +7824,7 @@
 msgstr "показване на недостижимите обекти"
 
 msgid "show dangling objects"
-msgstr "показване на обектите извън клоните"
+msgstr "показване на обектите извън кло̀ните"
 
 msgid "report tags"
 msgstr "показване на етикетите"
@@ -7719,7 +7848,7 @@
 msgstr "по-строги проверки"
 
 msgid "write dangling objects in .git/lost-found"
-msgstr "запазване на обектите извън клоните в директорията „.git/lost-found“"
+msgstr "запазване на обектите извън кло̀ните в директорията „.git/lost-found“"
 
 msgid "show progress"
 msgstr "показване на напредъка"
@@ -7943,7 +8072,7 @@
 msgstr "графът с подаванията не може да се запише"
 
 msgid "failed to prefetch remotes"
-msgstr "неуспешно предварително доставяне на отдалечените клони"
+msgstr "неуспешно предварително доставяне на отдалечените кло̀ни"
 
 msgid "failed to start 'git pack-objects' process"
 msgstr "процесът за командата „git pack-objects“ не може да се стартира"
@@ -8034,7 +8163,7 @@
 
 #, c-format
 msgid "failed to create directories for '%s'"
-msgstr "директориите за „%s“ не може да бъдат създадени"
+msgstr "директориите за „%s“ не може да се създадат"
 
 #, c-format
 msgid "failed to bootstrap service %s"
@@ -8098,7 +8227,7 @@
 "then try again. If it still fails, a git-maintenance(1) process may have\n"
 "crashed in this repository earlier: remove the file manually to continue."
 msgstr ""
-"Файлът-ключалка „%s.lock“ не може да се създаде: %s\n"
+"Заключващият файл „%s.lock“ не може да се създаде: %s\n"
 "\n"
 "Изглежда, че и друг процес на git-maintenance(1) е пуснат в това\n"
 "хранилище.  Уверете се, че всички подобни процеси са спрени и опитайте\n"
@@ -8107,7 +8236,7 @@
 "ще трябва ръчно да изтриете файла:"
 
 msgid "cannot acquire lock for scheduled background maintenance"
-msgstr "не може да се придобие ключалката за поддръжката на заден фон"
+msgstr "заключващият файл за поддръжката на заден фон не може да се заключи"
 
 msgid "git maintenance start [--scheduler=<scheduler>]"
 msgstr "git maintenance start [--scheduler=ПЛАНИРАЩ_МОДУЛ]"
@@ -8487,11 +8616,160 @@
 msgid "'git help config' for more information"
 msgstr "За повече информация изпълнете „git help config“"
 
-msgid ""
-"git hook run [--ignore-missing] [--to-stdin=<path>] <hook-name> [-- <hook-"
-"args>]"
+msgid "git history reword <commit> [--dry-run] [--update-refs=(branches|head)]"
 msgstr ""
-"git hook run [--ignore-missing] [--to-stdin=ПЪТ] КУКА [-- АРГУМЕНТ_ЗА_КУКА…]"
+"git history reword ПОДАВАНЕ [--dry-run] [--update-refs=(branches|head)]"
+
+msgid ""
+"git history split <commit> [--dry-run] [--update-refs=(branches|head)] [--] "
+"[<pathspec>...]"
+msgstr ""
+"git history split ПОДАВАНЕ [--dry-run] [--update-refs=(branches|head)] [--] "
+"[ШАБЛОН_ЗА_ПЪТ…]"
+
+#, c-format
+msgid ""
+"Please enter the commit message for the %s changes. Lines starting\n"
+"with '%s' will be ignored, and an empty message aborts the commit.\n"
+msgstr ""
+"Въведете съобщението за подаване на промѐните за %s.  Редовете, които "
+"започват\n"
+"с „%s“, ще се пропуснат, а празно съобщение преустановява подаването.\n"
+
+#, c-format
+msgid "Aborting commit as launching the editor failed.\n"
+msgstr ""
+"Преустановяване на подаване поради неуспешно стартиране на текстовия "
+"редактор.\n"
+
+#, c-format
+msgid "unable to parse parent commit %s"
+msgstr "родителското подаване не може да се анализира: %s"
+
+#, c-format
+msgid "%s expects one of 'branches' or 'head'"
+msgstr "опцията „%s“ изисква „branches“ или „head“"
+
+msgid "error preparing revisions"
+msgstr "грешка при подготовката на версии"
+
+msgid "replaying merge commits is not supported yet!"
+msgstr "прилагането наново на подавания със сливане не се поддържа!"
+
+msgid "cannot look up HEAD"
+msgstr "указателят „HEAD“ липсва"
+
+msgid "cannot determine descendance"
+msgstr "наследниците не може да се определят"
+
+msgid ""
+"rewritten commit must be an ancestor of HEAD when using --update-refs=head"
+msgstr ""
+"презаписаното подаване трябва да е предшественик на „HEAD“ при ползването на "
+"„--update-refs=head“"
+
+#, c-format
+msgid "failed to begin ref transaction: %s"
+msgstr "неуспешно начало на транзакция за указатели: %s"
+
+#, c-format
+msgid "failed to update ref '%s': %s"
+msgstr "указателят „%s“ не може да се обнови: %s"
+
+#, c-format
+msgid "failed to commit ref transaction: %s"
+msgstr "транзакцията за указатели не може да се завърши: %s"
+
+msgid "control which refs should be updated"
+msgstr "кои указатели да се обновят"
+
+msgid "perform a dry-run without updating any refs"
+msgstr "пробно изпълнение без промяна на указатели"
+
+msgid "command expects a single revision"
+msgstr "командата изисква точно една версия"
+
+#, c-format
+msgid "commit cannot be found: %s"
+msgstr "подаването липсва: %s"
+
+msgid "failed writing reworded commit"
+msgstr "подаването с променено съобщение не може да се презапише"
+
+msgid "failed replaying descendants"
+msgstr "неуспешно повторно прилагане на наследниците"
+
+msgid "unable to populate index with tree"
+msgstr "индексът не може да се попълни с дървото"
+
+msgid "unable to acquire index lock"
+msgstr "ключалката за индекса не може да се заключи"
+
+msgid "failed reading temporary index"
+msgstr "временен индекс не може да се прочете"
+
+msgid "failed split tree"
+msgstr "дървото не може да се разделѝ"
+
+msgid "split commit is empty"
+msgstr "празно подаване за разделяне"
+
+msgid "split commit tree matches original commit"
+msgstr "разделеното дърво на подаването съвпада с първоначалното подаване"
+
+msgid "failed writing first commit"
+msgstr "неуспешно запазване на начално подаване"
+
+msgid "failed writing second commit"
+msgstr "неуспешно запазване на второ подаване"
+
+msgid "control ref update behavior"
+msgstr "поведение при обновяването на указател"
+
+msgid "command expects a committish"
+msgstr "командата очаква подаване"
+
+msgid "cannot split up merge commit"
+msgstr "подаване за сливане не може да се разделѝ"
+
+msgid ""
+"git hook run [--allow-unknown-hook-name] [--ignore-missing] [--to-"
+"stdin=<path>] <hook-name> [-- <hook-args>]"
+msgstr ""
+"git hook run [--allow-unknown-hook-name] [--ignore-missing] [--to-stdin=ПЪТ] "
+"КУКА [-- АРГУМЕНТ_ЗА_КУКА…]"
+
+msgid ""
+"git hook list [--allow-unknown-hook-name] [-z] [--show-scope] <hook-name>"
+msgstr "git hook list [--allow-unknown-hook-name] [-z] [--show-scope] КУКА"
+
+msgid "use NUL as line terminator"
+msgstr "редовете да са разделени с нулевия знак „NUL“"
+
+msgid "show the config scope that defined each hook"
+msgstr "извеждане на обхвата на настройките, които всяка кука дефинира"
+
+msgid "allow running a hook with a non-native hook name"
+msgstr "позволяване на изпълнение на кука с чуждо име на кука"
+
+msgid "you must specify a hook event name to list"
+msgstr "трябва да укажете името на събитие за кука за извеждане"
+
+#, c-format
+msgid ""
+"unknown hook event '%s';\n"
+"use --allow-unknown-hook-name to allow non-native hook names"
+msgstr ""
+"непознато събитие за кука: „%s“;\n"
+"ползвайте опцията „--allow-unknown-hook-name“ за да позволите чужди имена на "
+"куки"
+
+#, c-format
+msgid "no hooks found for event '%s'"
+msgstr "няма куки за събитието обекта „%s“"
+
+msgid "hook from hookdir"
+msgstr "кука от директорията за куки"
 
 msgid "silently ignore missing requested <hook-name>"
 msgstr "прескачане на заявена КУКА, която липсва"
@@ -8596,7 +8874,7 @@
 
 #, c-format
 msgid "Not all child objects of %s are reachable"
-msgstr "Някои обекти, наследници на „%s“, не може да бъдат достигнати"
+msgstr "Някои обекти, наследници на „%s“, не може да се достигнат"
 
 msgid "failed to apply delta"
 msgstr "разликата не може да се приложи"
@@ -8807,21 +9085,6 @@
 "                       [--parse] [ФАЙЛ…]"
 
 #, c-format
-msgid "could not stat %s"
-msgstr "Не може да се получи информация чрез „stat“ за „%s“"
-
-#, c-format
-msgid "file %s is not a regular file"
-msgstr "„%s“ не е обикновен файл"
-
-#, c-format
-msgid "file %s is not writable by user"
-msgstr "„%s“: няма права̀ за записване на файла"
-
-msgid "could not open temporary file"
-msgstr "временният файл не може да се отвори"
-
-#, c-format
 msgid "could not read input file '%s'"
 msgstr "входният файл „%s“ не може да се прочете"
 
@@ -8829,6 +9092,10 @@
 msgstr "от стандартния вход не може да се чете"
 
 #, c-format
+msgid "could not write to temporary file '%s'"
+msgstr "във временния файл „%s“ не може да се пише"
+
+#, c-format
 msgid "could not rename temporary file to %s"
 msgstr "временният файл не може да се преименува на „%s“"
 
@@ -8853,8 +9120,9 @@
 msgid "output only the trailers"
 msgstr "извеждане само на епилозите"
 
-msgid "do not apply trailer.* configuration variables"
-msgstr "без прилагане на конфигуриращи променливи, завършващи с епилог.*"
+msgid "do not apply trailer.<key-alias> configuration variables"
+msgstr ""
+"без прилагане на конфигуриращи променливи, завършващи с trailer.СИНОНИМ"
 
 msgid "reformat multiline trailer values as single-line values"
 msgstr ""
@@ -8876,22 +9144,23 @@
 msgid "no input file given for in-place editing"
 msgstr "не е зададен входен файл за редактиране на място"
 
-msgid "last-modified can only operate on one tree at a time"
-msgstr "командата „last-modified“ работи само с по едно дърво"
+msgid "last-modified can only operate on one commit at a time"
+msgstr "командата „last-modified“ работи само с по едно подаване"
+
+#, c-format
+msgid "revision argument '%s' is a %s, not a commit-ish"
+msgstr "аргументът за версия „%s“ е %s, а не указател към подаване"
 
 #, c-format
 msgid "unknown last-modified argument: %s"
 msgstr "неправилен аргумент за командата „last-modified“: %s"
 
-msgid "unable to setup last-modified"
-msgstr "командата „last-modified“ не може да се настрои"
-
 msgid ""
-"git last-modified [--recursive] [--show-trees] [<revision-range>] [[--] "
-"<path>...]"
+"git last-modified [--recursive] [--show-trees] [--max-depth=<depth>] [-z]\n"
+"                  [<revision-range>] [[--] <pathspec>...]"
 msgstr ""
-"git last-modified [--recursive] [--show-trees] [ДИАПАЗОН_НА_ВЕРСИИТЕ] [[--] "
-"ПЪТ…]"
+"git last-modified [--recursive] [--show-trees] [--max-depth=ДЪЛБОЧИНА] [-z]\n"
+"                  [ДИАПАЗОН_НА_ВЕРСИИТЕ] [[--] ПЪТ…]"
 
 msgid "recurse into subtrees"
 msgstr "рекурсивно обхождане поддърветата"
@@ -8899,6 +9168,12 @@
 msgid "show tree entries when recursing into subtrees"
 msgstr "извеждане на дърветата при рекурсивното обхождане на поддърветата"
 
+msgid "maximum tree depth to recurse"
+msgstr "максимална дълбочина на дървото за обхождане"
+
+msgid "lines are separated with NUL character"
+msgstr "редовете са разделени с нулевия знак „NUL“"
+
 msgid "git log [<options>] [<revision-range>] [[--] <path>...]"
 msgstr "git log [ОПЦИЯ…] [ДИАПАЗОН_НА_ВЕРСИИТЕ] [[--] ПЪТ…]"
 
@@ -8946,7 +9221,7 @@
 "\n"
 msgstr ""
 "\n"
-"ПОДСКАЗКА: Може да замените „git whatchanged ОПЦИЯ…“ с:\n"
+"ПОДСКАЗКА: Може да заменѝте „git whatchanged ОПЦИЯ…“ с:\n"
 "ПОДСКАЗКА:     git log ОПЦИЯ… --raw --no-merges\n"
 "ПОДСКАЗКА: А може да създадете и псевдоним:\n"
 "ПОДСКАЗКА:    git config set --global alias.whatchanged 'log --raw --no-"
@@ -8973,6 +9248,19 @@
 msgstr "не е зададена стойност на „format.headers“"
 
 #, c-format
+msgid "bad boolean config value '%s' for '%s'"
+msgstr "неправилна булева стойност „%s“ за „%s“"
+
+#, c-format
+msgid ""
+"'%s' used to accept any value and treat that as 'true'.\n"
+"Now it only accepts boolean values, like what '%s' does.\n"
+msgstr ""
+"„%s“ се ползва за приемане на произволна стойност и обработването ѝ като "
+"ИСТИНА.\n"
+"В момента се приемат само булеви стойности, както прави „%s“.\n"
+
+#, c-format
 msgid "cannot open patch file %s"
 msgstr "файлът-кръпка „%s“ не може да се отвори"
 
@@ -8993,6 +9281,10 @@
 msgstr "неуспешно създаване на придружаващо писмо"
 
 #, c-format
+msgid "'%s' is not a valid format string"
+msgstr "„%s“ е неправилен форматиращ низ"
+
+#, c-format
 msgid "insane in-reply-to: %s"
 msgstr "неправилен формат на заглавната част за отговор „in-reply-to“: %s"
 
@@ -9056,6 +9348,12 @@
 msgid "generate a cover letter"
 msgstr "създаване на придружаващо писмо"
 
+msgid "format-spec"
+msgstr "ФОРМАТ"
+
+msgid "format spec used for the commit list in the cover letter"
+msgstr "ФОРМАТ за списъка с подавания в придружаващото писмо"
+
 msgid "use simple number sequence for output file names"
 msgstr "проста числова последователност за имената на файловете-кръпки"
 
@@ -9303,7 +9601,7 @@
 msgstr "извеждане на името на обекта за съдържанието на индекса"
 
 msgid "show files on the filesystem that need to be removed"
-msgstr "извеждане на файловете, които трябва да бъдат изтрити"
+msgstr "извеждане на файловете, които трябва да се изтрият"
 
 msgid "show 'other' directories' names only"
 msgstr "извеждане само на името на другите (неследените) директории"
@@ -9385,7 +9683,7 @@
 msgstr "само етикетите"
 
 msgid "limit to branches"
-msgstr "само клоните"
+msgstr "само кло̀ните"
 
 msgid "deprecated synonym for --branches"
 msgstr "остарял синоним на „--branches“"
@@ -9780,7 +10078,7 @@
 "Lines starting with '%s' will be ignored, and an empty message aborts\n"
 "the commit.\n"
 msgstr ""
-"Редовете, които започват с „%s“, ще бъдат пропуснати, а празно\n"
+"Редовете, които започват с „%s“, ще се пропуснат, а празно\n"
 "съобщение преустановява подаването.\n"
 
 msgid "Empty commit message."
@@ -9961,11 +10259,20 @@
 msgstr "разрешаване на създаването на повече от едно дърво"
 
 msgid ""
-"git multi-pack-index [<options>] write [--preferred-pack=<pack>][--refs-"
-"snapshot=<path>]"
+"git multi-pack-index [<options>] write [--preferred-pack=<pack>]\n"
+"  [--[no-]bitmap] [--[no-]incremental] [--[no-]stdin-packs]\n"
+"  [--refs-snapshot=<path>]"
 msgstr ""
-"git multi-pack-index [ОПЦИЯ…] write [--preferred-pack=ПАКЕТ] [--refs-"
-"snapshot=ПЪТ]"
+"git multi-pack-index [ОПЦИЯ…] write [--preferred-pack=ПАКЕТ]\n"
+"  [--[no-]bitmap] [--[no-]incremental] [--[no-]stdin-packs]\n"
+"  [--refs-snapshot=ПЪТ]"
+
+msgid ""
+"git multi-pack-index [<options>] compact [--[no-]incremental]\n"
+"  [--[no-]bitmap] <from> <to>"
+msgstr ""
+"git multi-pack-index [ОПЦИЯ…] compact [--[no-]incremental]\n"
+"  [--[no-]bitmap] ОТ ДО"
 
 msgid "git multi-pack-index [<options>] verify"
 msgstr "git multi-pack-index [ОПЦИЯ…] verify"
@@ -10005,6 +10312,17 @@
 msgid "refs snapshot for selecting bitmap commits"
 msgstr "снимка на указателите за избор на подавания по битова маска"
 
+#, c-format
+msgid "could not find MIDX: %s"
+msgstr "липсва индекс за множество пакети: %s"
+
+msgid "MIDX compaction endpoints must be unique"
+msgstr "крайните точки на индекс за множество пакети трябва да са уникални"
+
+#, c-format
+msgid "MIDX %s must be an ancestor of %s"
+msgstr "индексът за множество пакети „%s“ трябва да предшества „%s“"
+
 msgid ""
 "during repack, collect pack-files of smaller size into a batch that is "
 "larger than this size"
@@ -10123,8 +10441,7 @@
 
 msgid "list all commits reachable from all refs"
 msgstr ""
-"извеждане на всички подавания, които може да бъдат достигнати от всички "
-"указатели"
+"извеждане на всички подавания, които може да се достигнат от всички указатели"
 
 msgid "deprecated: use --annotate-stdin instead"
 msgstr "ОСТАРЯЛО: вместо това ползвайте „--annotate-stdin“"
@@ -10369,7 +10686,7 @@
 msgstr "указателят „NOTES_MERGE_PARTIAL“ не може да се прочете"
 
 msgid "could not find commit from NOTES_MERGE_PARTIAL."
-msgstr "подаването от „NOTES_MERGE_PARTIAL“ не може да се открие."
+msgstr "подаването от „NOTES_MERGE_PARTIAL“ липсва."
 
 msgid "could not parse commit from NOTES_MERGE_PARTIAL."
 msgstr "подаването от „NOTES_MERGE_PARTIAL“ не може да се анализира."
@@ -10501,8 +10818,8 @@
 "write_reuse_object: could not locate %s, expected at offset %<PRIuMAX> in "
 "pack %s"
 msgstr ""
-"write_reuse_object: „%s“ не може да се открие, а се очакваше при отместване "
-"%<PRIuMAX> в пакетния файл „%s“"
+"write_reuse_object: „%s“ липсва, а се очакваше при отместване %<PRIuMAX> в "
+"пакетния файл „%s“"
 
 #, c-format
 msgid "bad packed object CRC for %s"
@@ -10589,8 +10906,7 @@
 #, c-format
 msgid "unable to pack objects reachable from tag %s"
 msgstr ""
-"обектите, които може да бъдат достигнати от етикета „%s“, не може да бъдат "
-"пакетирани"
+"обектите, които може да се достигнат от етикета „%s“, не може да се пакетират"
 
 #, c-format
 msgid "unable to get type of object %s"
@@ -10636,15 +10952,15 @@
 msgstr "видът на обекта „%s“ в пакет „%s“ не може да се определи"
 
 #, c-format
+msgid "could not find pack '%s'"
+msgstr "пакетът „%s“ липсва"
+
+#, c-format
 msgid "packfile %s is a promisor but --exclude-promisor-objects was given"
 msgstr ""
 "пакетът „%s“ е гарантиращ, но е зададена опцията „--exclude-promisor-objects“"
 
 #, c-format
-msgid "could not find pack '%s'"
-msgstr "пакетът „%s“ не може да се открие"
-
-#, c-format
 msgid "packfile %s cannot be accessed"
 msgstr "пакетният файл „%s“ не може да се достъпи"
 
@@ -10673,11 +10989,6 @@
 "очаква се идентификатор на обект, а не повредени данни:\n"
 " %s"
 
-msgid "could not load cruft pack .mtimes"
-msgstr ""
-"времената на промяна (.mtimes) на пакета с ненужни файлове не може да се "
-"заредят"
-
 msgid "cannot open pack index"
 msgstr "индексът на пакетния файл не може да се отвори"
 
@@ -10700,7 +11011,7 @@
 msgstr "неправилна версия „%s“"
 
 msgid "unable to add recent objects"
-msgstr "скорошните обекти не може да бъдат добавени"
+msgstr "скорошните обекти не може да се добавят"
 
 #, c-format
 msgid "unsupported index version %s"
@@ -10784,8 +11095,8 @@
 
 msgid "include tag objects that refer to objects to be packed"
 msgstr ""
-"включване и на обектите-етикети, които сочат към обектите, които ще бъдат "
-"пакетирани"
+"включване и на обектите-етикети, които сочат към обектите, които ще се "
+"пакетират"
 
 msgid "keep unreachable objects"
 msgstr "запазване на недостижимите обекти"
@@ -10977,7 +11288,7 @@
 "да укажете отдалечения клон на командния ред."
 
 msgid "You are not currently on a branch."
-msgstr "Извън всички клони."
+msgstr "Извън всички кло̀ни."
 
 msgid "Please specify which branch you want to rebase against."
 msgstr "Укажете върху кой клон искате да пребазирате."
@@ -11037,7 +11348,7 @@
 "or --ff-only on the command line to override the configured default per\n"
 "invocation.\n"
 msgstr ""
-"Някои от клоните са се раздалечили и трябва да укажете как да се решава\n"
+"Някои от кло̀ните са се раздалечили и трябва да укажете как да се решава\n"
 "това.  За да заглушите това съобщение, изпълнете някоя от следните\n"
 "команди преди следващото издърпване:\n"
 "\n"
@@ -11120,7 +11431,7 @@
 "за връщане към нормално състояние."
 
 msgid "Cannot merge multiple branches into empty head."
-msgstr "Не може да сливате множество клони в празен върхов указател."
+msgstr "Не може да сливате множество кло̀ни в празен върхов указател."
 
 msgid "Cannot rebase onto multiple branches."
 msgstr "Не може да пребазирате върху повече от един клон."
@@ -11130,7 +11441,7 @@
 
 msgid "Need to specify how to reconcile divergent branches."
 msgstr ""
-"Трябва да укажете как да се решават разликите при раздалечаване на клоните."
+"Трябва да укажете как да се решават разликите при раздалечаване на кло̀ните."
 
 msgid "cannot rebase with locally recorded submodule modifications"
 msgstr ""
@@ -11160,7 +11471,7 @@
 "in 'git help config'.\n"
 msgstr ""
 "\n"
-"За да избегнете автоматичното задаване на следени клони, когато името им\n"
+"За да избегнете автоматичното задаване на следени кло̀ни, когато името им\n"
 "не съвпада с това на локалния клон, погледнете документацията за стойността\n"
 "„simple“ на настройката „branch.autoSetupMerge“ в „git help config“.\n"
 
@@ -11207,7 +11518,7 @@
 "upstream, see 'push.autoSetupRemote' in 'git help config'.\n"
 msgstr ""
 "\n"
-"За автоматично задаване за клони без следен клон, погледнете документацията "
+"За автоматично задаване за кло̀ни без следен клон, погледнете документацията "
 "на настройката „push.autoSetupRemote“ в „git help config“.\n"
 
 #, c-format
@@ -11345,7 +11656,7 @@
 msgstr "хранилище"
 
 msgid "push all branches"
-msgstr "изтласкване на всички клони"
+msgstr "изтласкване на всички кло̀ни"
 
 msgid "mirror all refs"
 msgstr "огледално копие на всички указатели"
@@ -11646,7 +11957,7 @@
 "instead, which does the same thing."
 msgstr ""
 "ползването на „--rebase-merges“ с празен низ за аргумент е остаряло и в "
-"бъдеще ще спре да работи. Ползвайте опцията „--rebase-merges“ без аргумент, "
+"бъдеще ще спре да работи.  Ползвайте опцията „--rebase-merges“ без аргумент, "
 "за да постигнете същия резултат."
 
 #, c-format
@@ -11765,7 +12076,7 @@
 "преместване на подаванията, които започват със „squash!“/“fixup!“ при „-i“"
 
 msgid "update branches that point to commits that are being rebased"
-msgstr "обновяване на клоните, които сочат към пребазирани подавания"
+msgstr "обновяване на кло̀ните, които сочат към пребазирани подавания"
 
 msgid "add exec lines after each commit of the editable list"
 msgstr ""
@@ -11849,7 +12160,7 @@
 "отбележете коригирането им чрез командата „git add“"
 
 msgid "could not discard worktree changes"
-msgstr "промѐните в работното дърво не може да бъдат занулени"
+msgstr "промѐните в работното дърво не може да се занулят"
 
 #, c-format
 msgid "could not move back to %s"
@@ -12308,7 +12619,7 @@
 "отдалечено хранилище „%s“ като подниз"
 
 msgid "fetch the remote branches"
-msgstr "отдалечените клони не може да бъдат доставени"
+msgstr "отдалечените кло̀ни не може да се доставят"
 
 msgid ""
 "import all tags and associated objects when fetching\n"
@@ -12333,7 +12644,7 @@
 
 msgid "specifying branches to track makes sense only with fetch mirrors"
 msgstr ""
-"указването на следени клони е смислено само за отдалечени хранилища, от "
+"указването на следени кло̀ни е смислено само за отдалечени хранилища, от "
 "които се доставя"
 
 #, c-format
@@ -12354,7 +12665,7 @@
 
 #, c-format
 msgid "Could not get fetch map for refspec %s"
-msgstr "Обектите за доставяне за указателя „%s“ не може да бъдат получени"
+msgstr "Обектите за доставяне за указателя „%s“ не може да се получат"
 
 msgid "(matching)"
 msgstr "(съвпадащи)"
@@ -12419,7 +12730,7 @@
 "\t%s\n"
 "\tPlease update the configuration manually if necessary."
 msgstr ""
-"Нестандартните указатели за доставяне няма да бъдат обновени\n"
+"Нестандартните указатели за доставяне няма да се обновят\n"
 "    %s\n"
 "  Променете настройките ръчно, ако е необходимо."
 
@@ -12455,7 +12766,7 @@
 msgstr " прескочен"
 
 msgid " stale (use 'git remote prune' to remove)"
-msgstr " стар (изтрийте чрез „git remote prune“)"
+msgstr " с изтекла давност (изтрийте чрез „git remote prune“)"
 
 msgid " ???"
 msgstr " неясно състояние"
@@ -12566,7 +12877,7 @@
 msgid "  Remote branch:%s"
 msgid_plural "  Remote branches:%s"
 msgstr[0] "  Отдалечен клон:%s"
-msgstr[1] "  Отдалечени клони:%s"
+msgstr[1] "  Отдалечени кло̀ни:%s"
 
 msgid " (status not queried)"
 msgstr " (състоянието не бе проверено)"
@@ -12574,10 +12885,10 @@
 msgid "  Local branch configured for 'git pull':"
 msgid_plural "  Local branches configured for 'git pull':"
 msgstr[0] "  Локален клон настроен за издърпване чрез „git pull“:"
-msgstr[1] "  Локални клони настроени за издърпване чрез „git pull“:"
+msgstr[1] "  Локални кло̀ни настроени за издърпване чрез „git pull“:"
 
 msgid "  Local refs will be mirrored by 'git push'"
-msgstr "  Локалните указатели ще бъдат пренесени чрез „ push“"
+msgstr "  Локалните указатели ще се копират огледално чрез „git push“"
 
 #, c-format
 msgid "  Local ref configured for 'git push'%s:"
@@ -12620,7 +12931,7 @@
 
 msgid "Multiple remote HEAD branches. Please choose one explicitly with:"
 msgstr ""
-"Множество клони с върхове.  Изберете изрично някой от тях чрез командата:"
+"Множество кло̀ни с върхове.  Изберете изрично някой от тях чрез командата:"
 
 #, c-format
 msgid "Could not delete %s"
@@ -13011,87 +13322,59 @@
 msgstr "опцията „-l“ приема точно един шаблон"
 
 #, c-format
-msgid "'%s' is not a valid commit-ish for %s"
-msgstr "„%s“ не прилича на подаване за %s"
-
-msgid "need some commits to replay"
-msgstr "необходимо е да има подавания за прилагане отново"
-
-msgid "all positive revisions given must be references"
-msgstr "всички зададени положителни версии трябва да са указатели"
-
-msgid "argument to --advance must be a reference"
-msgstr "аргументът към „--advance“ трябва да е указател"
-
-msgid ""
-"cannot advance target with multiple sources because ordering would be ill-"
-"defined"
-msgstr ""
-"цели с множество източници не може да се придвижат напред, защото подредбата "
-"не е добре дефинирана"
-
-#, c-format
 msgid "invalid %s value: '%s'"
 msgstr "неправилна стойност за „%s“: „%s“"
 
 msgid ""
-"(EXPERIMENTAL!) git replay ([--contained] --onto <newbase> | --advance "
-"<branch>) [--ref-action[=<mode>]] <revision-range>"
+"(EXPERIMENTAL!) git replay ([--contained] --onto=<newbase> | --"
+"advance=<branch> | --revert=<branch>)\n"
+"[--ref=<ref>] [--ref-action=<mode>] <revision-range>"
 msgstr ""
-"(ЕКСПЕРИМЕНТАЛНО!) git replay ([--contained] --onto НОВА_БАЗА | --advance "
-"КЛОН) [--ref-action[=РЕЖИМ]] ДИАПАЗОН_ПОДАВАНИЯ…"
+"(ЕКСПЕРИМЕНТАЛНО!) git replay ([--contained] --onto=НОВА_БАЗА | --"
+"advance=КЛОН | --revert=КЛОН)\n"
+"[--ref=УКАЗАТЕЛ] [--ref-action[=РЕЖИМ]] ДИАПАЗОН_ПОДАВАНИЯ"
 
-msgid "make replay advance given branch"
-msgstr "прилагането наново придвижва дадения КЛОН напред"
+msgid "update all branches that point at commits in <revision-range>"
+msgstr ""
+"обновяване на всички кло̀ни, които сочат към подавания в ДИАПАЗОНа_ПОДАВАНИЯ"
 
 msgid "replay onto given commit"
 msgstr "прилагането наново върху даденото ПОДАВАНЕ"
 
-msgid "update all branches that point at commits in <revision-range>"
-msgstr ""
-"обновяване на всички клони, които сочат към подавания в ДИАПАЗОНа_ПОДАВАНИЯ"
+msgid "make replay advance given branch"
+msgstr "прилагането наново придвижва дадения КЛОН напред"
+
+msgid "revert commits onto given branch"
+msgstr "отменяне на подаванията на даден КЛОН"
+
+msgid "reference to update with result"
+msgstr "указател за обновяване с резултата"
 
 msgid "control ref update behavior (update|print)"
 msgstr ""
 "поведение при обновяването на указател: „update“ (обновяване)/„print“ "
 "(извеждане)"
 
-msgid "option --onto or --advance is mandatory"
-msgstr "изисква се някоя от опциите „--onto“ или „--advance“"
+msgid "exactly one of --onto, --advance, or --revert is required"
+msgstr ""
+"необходима е точно една от опциите „--onto“, „--advance“ или „--revert“"
 
 #, c-format
 msgid ""
 "some rev walking options will be overridden as '%s' bit in 'struct rev_info' "
 "will be forced"
 msgstr ""
-"някои опции за проследяване на указатели ще бъдат променени, защото битът "
-"„%s“ в структурата „struct rev_info“ има превес"
-
-#, c-format
-msgid "failed to begin ref transaction: %s"
-msgstr "неуспешно начало на транзакция за указатели: %s"
-
-msgid "error preparing revisions"
-msgstr "грешка при подготовката на версии"
-
-msgid "replaying down from root commit is not supported yet!"
-msgstr "прилагането наново от началното подаване не се поддържа"
-
-msgid "replaying merge commits is not supported yet!"
-msgstr "прилагането наново на подавания със сливане не се поддържа!"
-
-#, c-format
-msgid "failed to update ref '%s': %s"
-msgstr "указателят „%s“ не може да се обнови: %s"
-
-#, c-format
-msgid "failed to commit ref transaction: %s"
-msgstr "транзакцията за указатели не може да се завърши: %s"
+"някои опции за проследяване на указатели ще се променят, защото битът „%s“ в "
+"структурата „struct rev_info“ има превес"
 
 #, c-format
 msgid "key '%s' not found"
 msgstr "ключът „%s“ липсва"
 
+msgid "--keys can only be used with --format=lines or --format=nul"
+msgstr ""
+"опцията „--keys“ изисква някоя от опциите „--format=lines“ или „--format=nul“"
+
 #, c-format
 msgid "invalid format '%s'"
 msgstr "неправилен формат „%s“"
@@ -13105,6 +13388,12 @@
 msgid "print all keys/values"
 msgstr "извеждане на всички ключове/стойности"
 
+msgid "show keys"
+msgstr "извеждане на ключовете"
+
+msgid "--keys cannot be used with a <key> or --all"
+msgstr "опцията „--keys“ изисква КЛЮЧ или опцията „--all“"
+
 msgid "unsupported output format"
 msgstr "неподдържан изходен формат"
 
@@ -13118,7 +13407,7 @@
 msgstr "Брой"
 
 msgid "Branches"
-msgstr "Клони"
+msgstr "Кло̀ни"
 
 msgid "Tags"
 msgstr "Етикети"
@@ -13147,6 +13436,18 @@
 msgid "Disk size"
 msgstr "Размер на диска"
 
+msgid "Largest objects"
+msgstr "Най-големи обекти"
+
+msgid "Maximum size"
+msgstr "Максимален размер"
+
+msgid "Maximum parents"
+msgstr "Максимален брой родители"
+
+msgid "Maximum entries"
+msgstr "Максимален брой записи"
+
 msgid "Repository structure"
 msgstr "Структура на хранилището"
 
@@ -13205,11 +13506,11 @@
 msgstr "Указателят „HEAD“ е повреден."
 
 msgid "Failed to find tree of HEAD."
-msgstr "Дървото, сочено от указателя „HEAD“, не може да се открие."
+msgstr "Липсва дървото, сочено от указателя „HEAD“."
 
 #, c-format
 msgid "Failed to find tree of %s."
-msgstr "Дървото, сочено от „%s“, не може да се открие."
+msgstr "Липсва дървото, сочено от „%s“."
 
 #, c-format
 msgid "HEAD is now at %s"
@@ -13238,8 +13539,7 @@
 msgstr "зануляване на указателя „HEAD“, но запазване на локалните промѐни"
 
 msgid "record only the fact that removed paths will be added later"
-msgstr ""
-"отбелязване само на факта, че изтритите пътища ще бъдат добавени по-късно"
+msgstr "отбелязване само на факта, че изтритите пътища ще се добавят по-късно"
 
 #, c-format
 msgid "Failed to resolve '%s' as a valid revision."
@@ -13643,13 +13943,13 @@
 msgstr "никой указател не съвпада с „%s“"
 
 msgid "show remote-tracking and local branches"
-msgstr "извеждане на следящите и локалните клони"
+msgstr "извеждане на следящите и локалните кло̀ни"
 
 msgid "show remote-tracking branches"
-msgstr "извеждане на следящите клони"
+msgstr "извеждане на следящите кло̀ни"
 
 msgid "color '*!+-' corresponding to the branch"
-msgstr "оцветяване на „*!+-“ според клоните"
+msgstr "оцветяване на „*!+-“ според кло̀ните"
 
 msgid "show <n> more commits after the common ancestor"
 msgstr "извеждане на такъв БРОЙ подавания от общия предшественик"
@@ -13658,7 +13958,7 @@
 msgstr "псевдоним на „more=-1“"
 
 msgid "suppress naming strings"
-msgstr "без низове за имената на клоните"
+msgstr "без низове за имената на кло̀ните"
 
 msgid "include the current branch"
 msgstr "включване и на текущия клон"
@@ -13728,6 +14028,44 @@
 msgid "Unknown hash algorithm"
 msgstr "Непознат алгоритъм за контролни суми"
 
+msgid "assuming SHA-1; use --object-format to override"
+msgstr "приема се „SHA1“.  За смяна ползвайте опцията „--object-format“"
+
+msgid "unable to read header"
+msgstr "заглавната част не може да се прочете"
+
+msgid "unknown index version"
+msgstr "неподдържана версия на индекса"
+
+msgid "unable to read index"
+msgstr "индексът не може да се прочете"
+
+msgid "corrupt index file"
+msgstr "повреден файл за индекса"
+
+#, c-format
+msgid "unable to read entry %u/%u"
+msgstr "записът не може да се прочете: %u/%u"
+
+#, c-format
+msgid "unable to read sha1 %u/%u"
+msgstr "контролната сума във формат SHA1 не може да се прочете: %u/%u"
+
+#, c-format
+msgid "unable to read crc %u/%u"
+msgstr "контролната сума за проверка не може да се прочете: %u/%u"
+
+#, c-format
+msgid "unable to read 32b offset %u/%u"
+msgstr "32-битовото отместване не може да се прочете: %u/%u"
+
+msgid "inconsistent 64b offset index"
+msgstr "повредено 64-битово отместване в индекса"
+
+#, c-format
+msgid "unable to read 64b offset %u"
+msgstr "64-битовото отместване не може да се прочете: %u"
+
 msgid ""
 "git show-ref [--head] [-d | --dereference]\n"
 "             [-s | --hash[=<n>]] [--abbrev[=<n>]] [--branches] [--tags]\n"
@@ -13753,10 +14091,10 @@
 msgstr "git show-ref --exists УКАЗАТЕЛ"
 
 msgid "only show tags (can be combined with --branches)"
-msgstr "извеждане на етикетите (може да се комбинира с „--branches“ за клони)"
+msgstr "извеждане на етикетите (може да се комбинира с „--branches“ за кло̀ни)"
 
 msgid "only show branches (can be combined with --tags)"
-msgstr "извеждане на клоните (може да се комбинира с „--tags“ за етикети)"
+msgstr "извеждане на кло̀ните (може да се комбинира с „--tags“ за етикети)"
 
 msgid "check for reference existence without resolving"
 msgstr "проверка за съществуване на указател без проследяването му"
@@ -13908,8 +14246,8 @@
 
 msgid "must be in a sparse-checkout to reapply sparsity patterns"
 msgstr ""
-"шаблоните за частичност може да бъдат приложени наново само в частично "
-"изтеглено хранилище"
+"шаблоните за частичност може да се приложат наново само в частично изтеглено "
+"хранилище"
 
 msgid "report each affected file, not just directories"
 msgstr "извеждане на засегнатите файлове, а не само на директориите"
@@ -13989,18 +14327,18 @@
 msgstr "git stash store [-m|--message СЪОБЩЕНИЕ] [-q|--quiet] ПОДАВАНЕ"
 
 msgid ""
-"git stash [push [-p | --patch] [-S | --staged] [-k | --[no-]keep-index] [-q "
+"git stash [push] [-p | --patch] [-S | --staged] [-k | --[no-]keep-index] [-q "
 "| --quiet]\n"
 "          [-u | --include-untracked] [-a | --all] [(-m | --message) "
 "<message>]\n"
 "          [--pathspec-from-file=<file> [--pathspec-file-nul]]\n"
-"          [--] [<pathspec>...]]"
+"          [--] [<pathspec>...]"
 msgstr ""
-"git stash [push [-p|--patch] [-S|--staged] [-k|--[no-]keep-index] [-q | --"
+"git stash [push] [-p|--patch] [-S|--staged] [-k|--[no-]keep-index] [-q | --"
 "quiet]\n"
 "          [-u|--include-untracked] [-a|--all] [(-m|--message) СЪОБЩЕНИЕ]\n"
 "          [--pathspec-from-file=ФАЙЛ [--pathspec-file-nul]]\n"
-"          [--] [ШАБЛОН_ЗА_ПЪТИЩА…]]"
+"          [--] [ШАБЛОН_ЗА_ПЪТИЩА…]"
 
 msgid ""
 "git stash save [-p | --patch] [-S | --staged] [-k | --[no-]keep-index] [-q | "
@@ -14165,7 +14503,7 @@
 msgstr "Състоянието на работната директория и индекса e запазено: „%s“"
 
 msgid "Cannot remove worktree changes"
-msgstr "Промѐните в работното дърво не може да бъдат занулени"
+msgstr "Промѐните в работното дърво не може да се занулят"
 
 msgid "keep index"
 msgstr "запазване на индекса"
@@ -14237,7 +14575,7 @@
 
 #, c-format
 msgid "unable to find stash entry %s"
-msgstr "скатаният запис за „%s“ не може да се открие"
+msgstr "скатаният запис за „%s“ липсва"
 
 msgid "print the object ID instead of writing it to a ref"
 msgstr "извеждане на идентификатор на обект вместо запазването му в указател"
@@ -14246,7 +14584,7 @@
 msgstr "запазване на данните в дадения указател"
 
 msgid "exactly one of --print and --to-ref is required"
-msgstr "необходима е точно една от опциите „--print“ и „--to-ref“"
+msgstr "необходима е точно една от опциите „--print“ или „--to-ref“"
 
 msgid "skip and remove all lines starting with comment character"
 msgstr "пропускане на всички редове, които започват с „#“"
@@ -14266,6 +14604,9 @@
 msgid "could not get a repository handle for submodule '%s'"
 msgstr "не може да се получи връзка към хранилище за подмодула „%s“"
 
+msgid "git submodule--helper get-default-remote <path>"
+msgstr "git submodule--helper get-default-remote ПЪТ"
+
 #, c-format
 msgid "No url found for submodule path '%s' in .gitmodules"
 msgstr "Във файла „.gitmodules“ не е открит адрес за пътя към подмодул „%s“"
@@ -14302,6 +14643,17 @@
 msgstr "git submodule foreach [--quiet] [--recursive] [--] КОМАНДА"
 
 #, c-format
+msgid ""
+"failed to set a valid default config for 'submodule.%s.gitdir'. Please "
+"ensure it is set, for example by running something like: 'git config "
+"submodule.%s.gitdir .git/modules/%s'"
+msgstr ""
+"неуспешно задаване на стандартна стойност за настройката „submodule."
+"%s.gitdir“.  Уверете се, че е зададена, напр. като изпълнете командата:\n"
+"\n"
+"    git config submodule.%s.gitdir .git/modules/%s'"
+
+#, c-format
 msgid "Failed to register url for submodule path '%s'"
 msgstr "Неуспешно регистриране на адрес за пътя към подмодул „%s“"
 
@@ -14391,6 +14743,29 @@
 msgid "could not fetch a revision for HEAD"
 msgstr "не може да се достави версия за „HEAD“"
 
+msgid "git submodule--helper gitdir <name>"
+msgstr "git submodule--helper gitdir ИМЕ"
+
+msgid ""
+"could not set core.repositoryformatversion to 1.\n"
+"Please set it for migration to work, for example:\n"
+"git config core.repositoryformatversion 1"
+msgstr ""
+"настройката „core.repositoryformatversion“ не може де се зададе да е 1.\n"
+"Задайте я, за да сработи миграцията, напр. като изпълнете командата:\n"
+"\n"
+"    git config core.repositoryformatversion 1"
+
+msgid ""
+"could not enable submodulePathConfig extension. It is required\n"
+"for migration to work. Please enable it in the root repo:\n"
+"git config extensions.submodulePathConfig true"
+msgstr ""
+"настройката „submodulePathConfig“ не може де се включи.\n"
+"Задайте я, за да сработи миграцията, напр. като изпълнете командата:\n"
+"\n"
+"    git config extensions.submodulePathConfig true"
+
 #, c-format
 msgid "Synchronizing submodule url for '%s'\n"
 msgstr "Синхронизиране на адреса на подмодул за „%s“\n"
@@ -14487,12 +14862,6 @@
 msgstr "Непозната стойност „%s“ за настройката „submodule.alternateLocation“"
 
 #, c-format
-msgid "refusing to create/use '%s' in another submodule's git dir"
-msgstr ""
-"„%s“ не може нито да се създаде, нито да се ползва в директорията на git на "
-"друг подмодул"
-
-#, c-format
 msgid "directory not empty: '%s'"
 msgstr "директорията не е празна: „%s“"
 
@@ -14501,6 +14870,15 @@
 msgstr "Неуспешно клониране на адреса „%s“ в пътя „%s“ като подмодул"
 
 #, c-format
+msgid ""
+"refusing to create/use '%s' in another submodule's git dir. Enabling "
+"extensions.submodulePathConfig should fix this."
+msgstr ""
+"„%s“ не може нито да се създаде, нито да се ползва в директорията на git на "
+"друг подмодул.  Включването на „extensions.submodulePathConfig“ трябва да "
+"коригира това."
+
+#, c-format
 msgid "could not get submodule directory for '%s'"
 msgstr "директорията на подмодула „%s“ не може да се получи"
 
@@ -14916,7 +15294,7 @@
 "\n"
 "Въведете съобщение за етикета.\n"
 "    %s\n"
-"Редовете, които започват с „%s“, ще бъдат пропуснати.\n"
+"Редовете, които започват с „%s“, ще се пропуснат.\n"
 
 #, c-format
 msgid ""
@@ -14929,8 +15307,8 @@
 "\n"
 "Въведете съобщение за етикет.\n"
 "    %s\n"
-"Редовете, които започват с „%s“, също ще бъдат включени — може да ги "
-"изтриете вие.\n"
+"Редовете, които започват с „%s“, също ще се включат — може да ги изтриете "
+"вие.\n"
 
 msgid "unable to sign the tag"
 msgstr "етикетът не може да се подпише"
@@ -15424,8 +15802,8 @@
 msgid "report pruned working trees"
 msgstr "докладване на окастрените работни дървета"
 
-msgid "expire working trees older than <time>"
-msgstr "обявяване на работните копия по-стари от това ВРЕМЕ за остарели"
+msgid "prune missing working trees older than <time>"
+msgstr "окастряне на липсващите работните дървета по-стари от това ВРЕМЕ"
 
 #, c-format
 msgid "'%s' already exists"
@@ -15496,22 +15874,15 @@
 msgid "Preparing worktree (detached HEAD %s)"
 msgstr "Подготвяне на работно дърво (указателят „HEAD“ не свързан: %s)"
 
-#, c-format
-msgid ""
-"HEAD points to an invalid (or orphaned) reference.\n"
-"HEAD path: '%s'\n"
-"HEAD contents: '%s'"
-msgstr ""
-"HEAD сочи към неправилен или неродѐн указател.\n"
-"HEAD path: „%s“\n"
-"HEAD contents: „%s“"
+msgid "HEAD points to an invalid (or orphaned) reference.\n"
+msgstr "указателят „HEAD“ сочи към неправилен (или неродѐн) указател.\n"
 
 msgid ""
 "No local or remote refs exist despite at least one remote\n"
 "present, stopping; use 'add -f' to override or fetch a remote first"
 msgstr ""
 "Не съществуват никакви локални или отдалечени указатели, въпреки че има\n"
-"поне едно следено хранилище. Работата спира.\n"
+"поне едно следено хранилище.  Работата спира.\n"
 "Ползвайте комбинацията „add -f“ за принудително действие или първо "
 "доставете\n"
 "обектите от отдалеченото хранилище"
@@ -15523,7 +15894,7 @@
 msgstr "създаване на нов клон"
 
 msgid "create or reset a branch"
-msgstr "създаване или зануляване на клони"
+msgstr "създаване или зануляване на кло̀ни"
 
 msgid "create unborn branch"
 msgstr "създаване на неродѐн клон"
@@ -15563,9 +15934,10 @@
 msgid "show extended annotations and reasons, if available"
 msgstr "извеждане на подробни анотации и обяснения, ако такива са налични"
 
-msgid "add 'prunable' annotation to worktrees older than <time>"
+msgid "add 'prunable' annotation to missing worktrees older than <time>"
 msgstr ""
-"добавяне на анотация за окастряне на работните копия по-стари от това ВРЕМЕ"
+"добавяне на анотация за окастряне на липсващите работни дървета по-стари от "
+"това ВРЕМЕ"
 
 msgid "terminate records with a NUL character"
 msgstr "разделяне на записите с нулевия знак „NUL“"
@@ -15850,7 +16222,7 @@
 #, c-format
 msgid "cache-tree for path %.*s does not match. Expected %s got %s"
 msgstr ""
-"кешираният обект-дърво за пътя „%.*s“ не съвпада. Изисква се „%s“, а не „%s“"
+"кешираният обект-дърво за пътя „%.*s“ не съвпада.  Изисква се „%s“, а не „%s“"
 
 msgid "terminating chunk id appears earlier than expected"
 msgstr "идентификаторът за краен откъс се явява по-рано от очакваното"
@@ -15908,7 +16280,7 @@
 "файл"
 
 msgid "List, create, or delete branches"
-msgstr "Извеждане, създаване, изтриване на клони"
+msgstr "Извеждане, създаване, изтриване на кло̀ни"
 
 msgid "Collect information for user to file a bug report"
 msgstr "Събиране на информация за потребителя за доклад за грешка"
@@ -15977,14 +16349,15 @@
 "Преброяване на непакетираните обекти и колко дисково пространство заемат"
 
 msgid "Retrieve and store user credentials"
-msgstr "Получаване и запазване на идентификацията на потребител"
+msgstr "Получаване и запазване на идентификационните данни на потребител"
 
 msgid "Helper to temporarily store passwords in memory"
 msgstr "Помощна програма за временно запазване на пароли в паметта"
 
 msgid "Helper to store credentials on disk"
 msgstr ""
-"Помощна програма за запазване на идентификацията на потребител на диска"
+"Помощна програма за запазване на идентификационните данни на потребител на "
+"диска"
 
 msgid "Export a single commit to a CVS checkout"
 msgstr "Изнасяне на едно подаване като изтегляне в CVS"
@@ -16037,7 +16410,7 @@
 msgstr "Получаване на липсващи обекти от друго хранилище"
 
 msgid "Rewrite branches"
-msgstr "Презаписване на клони"
+msgstr "Презаписване на кло̀ни"
 
 msgid "Produce a merge commit message"
 msgstr "Създаване на съобщение при подаване със сливане"
@@ -16073,6 +16446,9 @@
 msgid "Display help information about Git"
 msgstr "Извеждане на помощта за Git"
 
+msgid "EXPERIMENTAL: Rewrite history"
+msgstr "ЕКСПЕРИМЕНТАЛНО: Презаписване на историята"
+
 msgid "Run git hooks"
 msgstr "Изпълнение на куки на git"
 
@@ -16176,8 +16552,8 @@
 msgid "Pack heads and tags for efficient repository access"
 msgstr "Пакетиране на върховете и етикетите за бърз достъп до хранилище"
 
-msgid "Compute unique ID for a patch"
-msgstr "Генериране на уникален идентификатор на кръпка"
+msgid "Compute unique IDs for patches"
+msgstr "Генериране на уникални идентификатори за кръпките"
 
 msgid "Prune all unreachable objects from the object database"
 msgstr "Окастряне на всички недостижими обекти в базата от данни за обектите"
@@ -16275,7 +16651,7 @@
 msgstr "Извеждане на различните видове обекти в Git"
 
 msgid "Show branches and their commits"
-msgstr "Извеждане на клоните и подаванията в тях"
+msgstr "Извеждане на кло̀ните и подаванията в тях"
 
 msgid "Show packed archive index"
 msgstr "Извеждане на индекса на пакетирания архив"
@@ -16408,7 +16784,7 @@
 msgstr "Куки на Git"
 
 msgid "Specifies intentionally untracked files to ignore"
-msgstr "Указване на неследени файлове, които да бъдат нарочно пренебрегвани"
+msgstr "Указване на неследени файлове, които да се прескачат нарочно"
 
 msgid "The Git repository browser"
 msgstr "Разглеждане на хранилище на Git"
@@ -16556,7 +16932,7 @@
 "грешка във веригата на гра̀фа с подаванията: ред „%s“ не е контролна сума"
 
 msgid "unable to find all commit-graph files"
-msgstr "някои файлове на гра̀фа с подаванията не може да бъдат открити"
+msgstr "някои файлове на гра̀фа с подаванията не може да се открият"
 
 msgid "invalid commit position. commit-graph is likely corrupt"
 msgstr ""
@@ -16628,7 +17004,7 @@
 
 #, c-format
 msgid "unable to adjust shared permissions for '%s'"
-msgstr "права̀та за споделен достъп до „%s“ не може да бъдат зададени"
+msgstr "права̀та за споделен достъп до „%s“ не може да се зададат"
 
 #, c-format
 msgid "Writing out commit graph in %d pass"
@@ -16747,6 +17123,10 @@
 msgstr "подаването „%s“ не може да се анализира"
 
 #, c-format
+msgid "object %s is a %s, not a %s"
+msgstr "обектът „%s“ е %s, а не %s"
+
+#, c-format
 msgid "%s %s is not a commit!"
 msgstr "%s %s не е подаване!"
 
@@ -16761,7 +17141,7 @@
 "\"git config set advice.graftFileDeprecated false\""
 msgstr ""
 "Поддръжката на „<GIT_DIR>/info/grafts“ е остаряла.\n"
-"В бъдеща версия на Git ще бъде премахната.\n"
+"В бъдеща версия на Git ще се премахне.\n"
 "\n"
 "Може да преобразувате присадките в заместващи\n"
 "указатели с командата:\n"
@@ -16891,8 +17271,8 @@
 #, c-format
 msgid "could not read directory changes [GLE %ld]"
 msgstr ""
-"промѐните по директориите не може да бъдат прочетени [последна грешка в "
-"нишката: GLE=%ld]"
+"промѐните по директориите не може да се прочетат [последна грешка в нишката: "
+"GLE=%ld]"
 
 #, c-format
 msgid "opendir('%s') failed"
@@ -17171,10 +17551,6 @@
 msgstr "неправилна числова стойност „%s“ за „%s“ в %s: %s"
 
 #, c-format
-msgid "bad boolean config value '%s' for '%s'"
-msgstr "неправилна булева стойност „%s“ за „%s“"
-
-#, c-format
 msgid "failed to expand user dir in: '%s'"
 msgstr "домашната папка на потребителя липсва: „%s“"
 
@@ -17232,7 +17608,7 @@
 
 #, c-format
 msgid "Support for '%s=auto' is deprecated and will be removed in Git 3.0"
-msgstr "Поддръжката на „%s=auto“ е остаряла и ще бъде премахната в Git 3.0"
+msgstr "Поддръжката на „%s=auto“ е остаряла и ще се премахне в Git 3.0"
 
 msgid "unknown error occurred while reading the configuration files"
 msgstr "неочаквана грешка при изчитането на конфигурационните файлове"
@@ -17442,7 +17818,7 @@
 
 msgid "no path specified; see 'git help pull' for valid url syntax"
 msgstr ""
-"не е указан път. Проверете синтаксиса с командата:\n"
+"не е указан път.  Проверете синтаксиса с командата:\n"
 "\n"
 "    git help pull"
 
@@ -17483,7 +17859,7 @@
 
 #, c-format
 msgid "CRLF would be replaced by LF in %s"
-msgstr "всяка последователност от знаци „CRLF“ ще бъдe заменена с „LF“ в „%s“."
+msgstr "всяка последователност от знаци „CRLF“ ще сe заменѝ с „LF“ в „%s“."
 
 #, c-format
 msgid ""
@@ -17491,12 +17867,12 @@
 "touches it"
 msgstr ""
 "следващия път, когато git работи в работното копие на „%s“, всяка "
-"последователност от знаци „CRLF“ ще бъдe заменена с „LF“"
+"последователност от знаци „CRLF“ ще се заменѝ с „LF“"
 
 #, c-format
 msgid "LF would be replaced by CRLF in %s"
 msgstr ""
-"всеки знак „LF“ ще бъдe заменен с последователността от знаци „CRLF“ в „%s“."
+"всеки знак „LF“ ще се заменѝ с последователността от знаци „CRLF“ в „%s“."
 
 #, c-format
 msgid ""
@@ -17504,7 +17880,7 @@
 "touches it"
 msgstr ""
 "следващия път, когато git работи в работното копие на „%s“, всеки знак „LF“ "
-"ще бъдe заменен с последователността от знаци „CRLF“"
+"ще сe заменѝ с последователността от знаци „CRLF“"
 
 #, c-format
 msgid "BOM is prohibited in '%s' if encoded as %s"
@@ -17591,7 +17967,9 @@
 
 #, c-format
 msgid "skipping credential lookup for key: credential.%s"
-msgstr "прескачане на търсенето на идентификация според ключа: „credential.%s“"
+msgstr ""
+"прескачане на търсенето на идентификационни данни според ключа: „credential."
+"%s“"
 
 msgid "refusing to work with credential missing host field"
 msgstr "адресът трябва задължително да съдържа хост"
@@ -17873,6 +18251,9 @@
 msgid "unknown value after ws-error-highlight=%.*s"
 msgstr "непозната стойност след „ws-error-highlight=%.*s“"
 
+msgid "--find-object requires a git repository"
+msgstr "„--find-object“ изисква хранилище на git"
+
 #, c-format
 msgid "unable to resolve '%s'"
 msgstr "„%s“ не може да се открие"
@@ -18262,9 +18643,6 @@
 msgid "<depth>"
 msgstr "ДЪЛБОЧИНА"
 
-msgid "maximum tree depth to recurse"
-msgstr "максимална дълбочина на дървото за обхождане"
-
 msgid "<file>"
 msgstr "ФАЙЛ"
 
@@ -18342,7 +18720,7 @@
 
 #, c-format
 msgid "could not create directories for %s"
-msgstr "директориите за „%s“ не може да бъдат създадени"
+msgstr "директориите за „%s“ не може да се създадат"
 
 #, c-format
 msgid "could not migrate git directory from '%s' to '%s'"
@@ -18418,7 +18796,7 @@
 msgid "must be one of nothing, matching, simple, upstream or current"
 msgstr ""
 "трябва да е една от следните стойности: „nothing“ (без изтласкване при липса "
-"на указател), „matching“ (всички клони със съвпадащи имена), „simple“ "
+"на указател), „matching“ (всички кло̀ни със съвпадащи имена), „simple“ "
 "(клонът със същото име, от който се издърпва), „upstream“ (клонът, от който "
 "се издърпва) или „current“ (клонът със същото име)"
 
@@ -18599,7 +18977,7 @@
 #.
 #, c-format
 msgid "expected no other sections to be sent after no '%s'"
-msgstr "очаква се след липса на „%s“ да не се се пращат други раздели"
+msgstr "очаква се след липса на „%s“ да не се се пращат други раздѐли"
 
 #, c-format
 msgid "error processing shallow info: %d"
@@ -18620,6 +18998,10 @@
 msgid "git fetch-pack: expected response end packet"
 msgstr "git fetch-pack: очаква се пакет за край на отговора"
 
+#, c-format
+msgid "couldn't resolve 'auto' filter '%s': %s"
+msgstr "автоматичният филтър (auto) не може да се открие „%s“: %s"
+
 msgid "no matching remote head"
 msgstr "липсва подходящ връх от отдалеченото хранилище"
 
@@ -18814,8 +19196,7 @@
 #, c-format
 msgid "ssh signing revocation file configured but not found: %s"
 msgstr ""
-"файлът за отхвърляне на подписи на ssh е настроен, но не може да се открие: "
-"%s"
+"настроеният в конфигурацията файл за отхвърляне на подписи на ssh липсва: %s"
 
 #, c-format
 msgid "bad/incompatible signature '%s'"
@@ -19054,6 +19435,18 @@
 "\n"
 "    git config set advice.ignoredHook false"
 
+#, c-format
+msgid "disabled hook '%s' has no command configured"
+msgstr "не е настроена команда за изключената кука „%s“"
+
+#, c-format
+msgid ""
+"'hook.%s.command' must be configured or 'hook.%s.event' must be removed; "
+"aborting."
+msgstr ""
+"трябва да настроите „hook.%s.command“ или да премахнете „hook.%s.event“, "
+"преустановяване на действието."
+
 msgid "not a git repository"
 msgstr "не е хранилище на Git"
 
@@ -19078,7 +19471,7 @@
 
 #, c-format
 msgid "Unsupported SSL backend '%s'. Supported SSL backends:"
-msgstr "Неподдържана реализация на SSL „%s“. Поддържат се:"
+msgstr "Неподдържана реализация на SSL „%s“.  Поддържат се:"
 
 #, c-format
 msgid "Could not set SSL backend to '%s': cURL was built without SSL backends"
@@ -19110,6 +19503,24 @@
 
 #, c-format
 msgid ""
+"response requested a delay greater than http.maxRetryTime (%ld > %ld seconds)"
+msgstr ""
+"отговорът изиска забавяне по-голямо от настроеното в „http.maxRetryTime“ "
+"(%ld > %ld секунди)"
+
+#, c-format
+msgid ""
+"configured http.retryAfter exceeds http.maxRetryTime (%ld > %ld seconds)"
+msgstr ""
+"настроената стойност в „http.retryAfter“ е по-голяма от тази в "
+"„http.maxRetryTime“ (%ld > %ld секунди)"
+
+#, c-format
+msgid "rate limited, waiting %ld seconds before retry"
+msgstr "скоростта е ограничена, за следващ опит се изчакват %ld секунди"
+
+#, c-format
+msgid ""
 "number too large to represent as curl_off_t on this platform: %<PRIuMAX>"
 msgstr ""
 "числото е прекалено голямо, за да се представи като „curl_off_t“ на тази "
@@ -19200,6 +19611,9 @@
 "\n"
 "(например: „git config imap.folder Drafts“)"
 
+msgid "'auto' filter not supported by this command"
+msgstr "автоматичният филтър (auto) не се поддържа от тази команда"
+
 msgid "expected 'tree:<depth>'"
 msgstr "очаква се „tree:ДЪЛБОЧИНА“"
 
@@ -19218,12 +19632,18 @@
 msgid "must escape char in sub-filter-spec: '%c'"
 msgstr "знак за екраниране в подфилтър: „%c“"
 
+msgid "an 'auto' filter cannot be combined"
+msgstr "автоматичeн филтър (auto) не може да се комбинира с други"
+
 msgid "expected something after combine:"
 msgstr "добавете нещо след комбинирането:"
 
 msgid "multiple filter-specs cannot be combined"
 msgstr "не може да комбинирате множество филтри"
 
+msgid "an 'auto' filter is incompatible with any other filter"
+msgstr "автоматичeн филтър (auto) е несъвместим с всички други филтри"
+
 msgid "unable to upgrade repository format to support partial clone"
 msgstr ""
 "не може да се извърши частично клониране, защото форматът на хранилището не "
@@ -19242,7 +19662,7 @@
 #, c-format
 msgid "unable to parse sparse filter data in %s"
 msgstr ""
-"данните от филтъра за частични изтегляния в „%s“ не може да бъдат анализирани"
+"данните от филтъра за частични изтегляния в „%s“ не може да се анализират"
 
 #, c-format
 msgid "entry '%s' in tree %s has tree mode, but is not a tree"
@@ -19257,26 +19677,48 @@
 msgstr "кореновото дърво за подаване „%s“ не може да се зареди"
 
 #, c-format
+msgid "could not write lock pid file '%s'"
+msgstr "заключващият файл с идентификатор на процес „%s“ не може да се запише"
+
+#, c-format
+msgid "malformed lock pid file '%s'"
+msgstr "неправилен заключващ файл с идентификатор на процес „%s“"
+
+#, c-format
 msgid ""
-"Unable to create '%s.lock': %s.\n"
+"Unable to create '%s': %s.\n"
 "\n"
-"Another git process seems to be running in this repository, e.g.\n"
-"an editor opened by 'git commit'. Please make sure all processes\n"
-"are terminated then try again. If it still fails, a git process\n"
-"may have crashed in this repository earlier:\n"
-"remove the file manually to continue."
 msgstr ""
-"Файлът-ключалка „%s.lock“ не може да се създаде: %s\n"
+"„%s“ не може да се създаде: %s.\n"
 "\n"
-"Изглежда, че и друг процес на git е пуснат в това хранилище, напр.\n"
-"редактор, стартиран с „git commit“.  Уверете се, че всички подобни\n"
-"процеси са спрени и опитайте отново.  Ако това не помогне, вероятната\n"
-"причина е, че някой процес на git в това хранилище е забил.  За да\n"
-"продължите работа, ще трябва ръчно да изтриете файла:"
+
+#, c-format
+msgid ""
+"Lock may be held by process %<PRIuMAX>; if no git process is running, the "
+"lock file may be stale (PIDs can be reused)"
+msgstr ""
+"Заключващият файл се държи от процес %<PRIuMAX>.  Ако не работи процес на "
+"git, вероятно файлът е с изтекла давност (идентификаторите на процеси се "
+"преизползват)"
+
+#, c-format
+msgid ""
+"Lock was held by process %<PRIuMAX>, which is no longer running; the lock "
+"file appears to be stale"
+msgstr ""
+"Заключващият файл се държи от процес %<PRIuMAX>, който вече не съществува.  "
+"Вероятно файлът е с изтекла давност."
+
+msgid ""
+"Another git process seems to be running in this repository, or the lock file "
+"may be stale"
+msgstr ""
+"Изглежда друг процес на git е стартиран в това хранилище, а може и "
+"заключващият файл да е с изтекла давност"
 
 #, c-format
 msgid "Unable to create '%s.lock': %s"
-msgstr "Файлът-ключалка „%s.lock“ не може да се създаде: %s"
+msgstr "Заключващият файл „%s.lock“ не може да се създаде: %s"
 
 msgid "unable to create temporary object directory"
 msgstr "не може да се създаде директория за временни обекти"
@@ -19536,8 +19978,9 @@
 " - resolve any other conflicts in the superproject\n"
 " - commit the resulting index in the superproject\n"
 msgstr ""
-"В момента рекурсивното сливане с модули поддържа само тривиалните случаи.\n"
-"Ще трябва ръчно да разрешавате конфликтите във всеки модул, в който се "
+"В момента рекурсивното сливане с подмодули поддържа само тривиалните "
+"случаи.\n"
+"Ще трябва ръчно да разрешавате конфликтите в подмодулите, в които се "
 "появят.\n"
 "Това може да се постигне чрез следните стъпки:\n"
 "%s — върнете се към обхващащия проект и изпълнете:\n"
@@ -19581,8 +20024,15 @@
 msgid "malformed line: %s"
 msgstr "неправилен ред: „%s“."
 
-msgid "could not load pack"
-msgstr "пакетът не може да се зареди"
+#, c-format
+msgid "could not load pack %d"
+msgstr "пакет %d не може да се зареди"
+
+msgid "too many packs, unable to compact"
+msgstr "прекалено много пакети, не може да се компресират"
+
+msgid "could not determine preferred pack"
+msgstr "предпочитаният пакет не може да се определи"
 
 #, c-format
 msgid "unable to link '%s' to '%s'"
@@ -19592,6 +20042,15 @@
 msgid "failed to clear multi-pack-index at %s"
 msgstr "индексът за множество пакети не може да се изчисти при „%s“"
 
+#, c-format
+msgid "unknown MIDX version: %d"
+msgstr "неизвестна версия на индекса за множество пакети: %d"
+
+msgid "cannot perform MIDX compaction with v1 format"
+msgstr ""
+"при ползване на формат v1 файловете с индекси за множество пакети не може да "
+"се компресират"
+
 msgid "ignoring existing multi-pack-index; checksum mismatch"
 msgstr ""
 "индексът за множество пакети се прескача, защото сумата за проверка не "
@@ -19619,7 +20078,7 @@
 
 #, c-format
 msgid "did not see pack-file %s to drop"
-msgstr "пакетният файл за триене „%s“ не може да се открие"
+msgstr "пакетният файл за триене „%s“ липсва"
 
 #, c-format
 msgid "preferred pack '%s' is expired"
@@ -19753,7 +20212,7 @@
 "сума"
 
 msgid "unable to find all multi-pack index files"
-msgstr "някои файлове на индекса за множество пакети не може да бъдат открити"
+msgstr "някои файлове на индекса за множество пакети не може да се открият"
 
 msgid "invalid MIDX object position, MIDX is likely corrupt"
 msgstr ""
@@ -19866,8 +20325,7 @@
 
 #, c-format
 msgid "Refusing to rewrite notes in %s (outside of refs/notes/)"
-msgstr ""
-"Бележките в „%s“ няма да бъдат презаписани, защото са извън „refs/notes/“."
+msgstr "Бележките в „%s“ няма да се презапишат, защото са извън „refs/notes/“."
 
 #. TRANSLATORS: The first %s is the name of
 #. the environment variable, the second %s is
@@ -19945,7 +20403,7 @@
 
 #, c-format
 msgid "unable to set permission to '%s'"
-msgstr "права̀та за достъп до „%s“ не може да бъдат зададени"
+msgstr "права̀та за достъп до „%s“ не може да се зададат"
 
 msgid "core.fsyncMethod = batch is unsupported on this platform"
 msgstr "„core.fsyncMethod = batch“ не се поддържа на тази платформа"
@@ -20135,7 +20593,7 @@
 "running \"git config set advice.objectNameWarning false\""
 msgstr ""
 "При нормална работа Git никога не създава указатели, които завършват\n"
-"с 40 шестнадесетични знака, защото стандартно те ще бъдат прескачани.\n"
+"с 40 шестнадесетични знака, защото стандартно те ще се прескачат.\n"
 "Възможно е такива указатели да са създадени случайно.  Например:\n"
 "\n"
 "    git switch -c $br $(git rev-parse …)\n"
@@ -20210,10 +20668,6 @@
 msgstr "неправилен вид обект: „%s“"
 
 #, c-format
-msgid "object %s is a %s, not a %s"
-msgstr "обектът „%s“ е %s, а не %s"
-
-#, c-format
 msgid "object %s has unknown type id %d"
 msgstr "обектът „%s“ е непознат вид: %d"
 
@@ -20222,6 +20676,10 @@
 msgstr "обектът „%s“ не може да се анализира"
 
 #, c-format
+msgid "unable to open object stream for %s"
+msgstr "потокът с обекти за „%s“ не може да се отвори"
+
+#, c-format
 msgid "hash mismatch %s"
 msgstr "разлика в контролната сума: „%s“"
 
@@ -20241,15 +20699,6 @@
 "%s: алтернативните хранилища за обекти се пренебрегват поради прекалено "
 "дълбоко влагане"
 
-msgid "unable to fdopen alternates lockfile"
-msgstr "заключващият файл за алтернативите не може да се отвори с „fdopen“"
-
-msgid "unable to read alternates file"
-msgstr "файлът с алтернативите не може да се прочете"
-
-msgid "unable to move new alternates file into place"
-msgstr "новият файл с алтернативите не може да се премести на мястото си"
-
 #, c-format
 msgid "path '%s' does not exist"
 msgstr "пътят „%s“ не съществува."
@@ -20272,7 +20721,7 @@
 
 #, c-format
 msgid "could not find object directory matching %s"
-msgstr "директорията с обекти, която отговаря на „%s“, не може да се открие"
+msgstr "липсва директорията с обекти, която отговаря на „%s“"
 
 #, c-format
 msgid "invalid line while parsing alternate refs: %s"
@@ -20294,6 +20743,15 @@
 msgid "%s is not a valid '%s' object"
 msgstr "„%s“ е неправилен обект от вид „%s“"
 
+msgid "unable to fdopen alternates lockfile"
+msgstr "заключващият файл за алтернативите не може да се отвори с „fdopen“"
+
+msgid "unable to read alternates file"
+msgstr "файлът с алтернативите не може да се прочете"
+
+msgid "unable to move new alternates file into place"
+msgstr "новият файл с алтернативите не може да се премести на мястото си"
+
 #, c-format
 msgid "duplicate entry when writing bitmap index: %s"
 msgstr "повтарящ се запис при запазване на индекс на база битови маски: „%s“"
@@ -20531,9 +20989,6 @@
 msgstr ""
 "неправилен размер на откъс за обратен индекс в индекса за множество пакети"
 
-msgid "could not determine preferred pack"
-msgstr "предпочитаният пакет не може да се определи"
-
 msgid "cannot both write and verify reverse index"
 msgstr "обратният индекс не може едновременно да се записва и да се проверява"
 
@@ -20558,6 +21013,12 @@
 msgstr "не може да се изпълни „mmap“ върху пакетния файл „%s“%s"
 
 #, c-format
+msgid "could not load .mtimes for cruft pack '%s'"
+msgstr ""
+"времената на промяна (.mtimes) на пакета с ненужни файлове „%s“ не може да "
+"се заредят"
+
+#, c-format
 msgid "offset before start of pack index for %s (corrupt index?)"
 msgstr ""
 "отместване преди началото на индекса на пакетния файл „%s“ (възможно е "
@@ -20913,6 +21374,10 @@
 msgid "unable to parse --pretty format"
 msgstr "аргументът към опцията „--pretty“ не може да се анализира"
 
+#, c-format
+msgid "%s is not supported by this command"
+msgstr "%s не се поддържа от тази команда"
+
 msgid "lazy fetching disabled; some objects may not be available"
 msgstr "отложеното доставяне е изключено, някои обекти може и да липсват"
 
@@ -20930,7 +21395,7 @@
 #, c-format
 msgid "promisor remote name cannot begin with '/': %s"
 msgstr ""
-"името отдалеченото хранилище-гарант не може за започва със знака „/“: %s"
+"името на отдалеченото хранилище-гарант не може за започва със знака „/“: %s"
 
 #, c-format
 msgid "could not fetch %s from promisor remote"
@@ -20957,6 +21422,24 @@
 msgstr "сървърът обяви за налично хранилище-гарант без име или адрес: %s"
 
 #, c-format
+msgid ""
+"Storing new %s from server for remote '%s'.\n"
+"    '%s' -> '%s'\n"
+msgstr ""
+"Запазване на нов %s от сървъра за отдалеченото хранилище „%s“.\n"
+"    „%s“ → „%s“\n"
+
+#, c-format
+msgid "invalid filter '%s' for remote '%s' will not be stored: %s"
+msgstr ""
+"неправилният филтър „%s“ за отдалеченото хранилище „%s“ няма да се запази: %s"
+
+#, c-format
+msgid "invalid token '%s' for remote '%s' will not be stored"
+msgstr ""
+"неправилната лексема „%s“ за отдалеченото хранилище „%s“ няма да се запази"
+
+#, c-format
 msgid "unknown '%s' value for '%s' config option"
 msgstr "непозната стойност „%s“ за настройката „%s“"
 
@@ -20964,6 +21447,10 @@
 msgid "accepted promisor remote '%s' not found"
 msgstr "липсва приетото вече хранилище-гарант „%s“"
 
+#, c-format
+msgid "promisor remote '%s' advertised invalid filter '%s': %s"
+msgstr "отдалеченото хранилище-гарант „%s“ обяви неправилен филтър „%s“: %s"
+
 msgid "object-info: expected flush after arguments"
 msgstr "object-info: след аргументите се очаква изчистване на буферите"
 
@@ -21226,13 +21713,21 @@
 
 #, c-format
 msgid "cannot fix permission bits on '%s'"
-msgstr "права̀та за достъп до „%s“ не може да бъдат поправени"
+msgstr "права̀та за достъп до „%s“ не може да се поправят"
 
 #, c-format
 msgid "%s: cannot drop to stage #0"
 msgstr "%s: не може да се премине към етап №0"
 
 #, c-format
+msgid ""
+"Skipping submodule due to ignore=all: %s\n"
+"Use --force if you really want to add the submodule."
+msgstr ""
+"Подмодулът се прескача заради „ignore=all“: %s\n"
+"Ако искате да добавите този подмодул, ползвайте „--force“."
+
+#, c-format
 msgid "unexpected diff status %c"
 msgstr "неочакван изходен код при генериране на разлика: %c"
 
@@ -21610,6 +22105,12 @@
 msgid "no reflog for '%s'"
 msgstr "липсва журнал с подаванията за „%s“"
 
+#, c-format
+msgid "in '%s' phase, update aborted by the reference-transaction hook"
+msgstr ""
+"във фаза „%s“, обновяването е преустановено от куката за транзакции на "
+"указатели (reference-transaction)"
+
 msgid "Checking references consistency"
 msgstr "Проверка на валидността на указателите"
 
@@ -21619,11 +22120,11 @@
 
 #, c-format
 msgid "%s%s will become dangling after %s is deleted\n"
-msgstr "%s„%s“ ще остане извън клоните след изтриването на „%s“\n"
+msgstr "%s„%s“ ще остане извън кло̀ните след изтриването на „%s“\n"
 
 #, c-format
 msgid "%s%s has become dangling after %s was deleted\n"
-msgstr "%s„%s“ остана извън клоните след изтриването на „%s“\n"
+msgstr "%s„%s“ остана извън кло̀ните след изтриването на „%s“\n"
 
 #, c-format
 msgid ""
@@ -21721,9 +22222,6 @@
 msgid "ref updates forbidden inside quarantine environment"
 msgstr "обновяванията на указатели са забранени в среди под карантина"
 
-msgid "ref updates aborted by hook"
-msgstr "обновяванията на указатели са преустановени от кука"
-
 #, c-format
 msgid "'%s' exists; cannot create '%s'"
 msgstr "„%s“ съществува, не може да се създаде „%s“"
@@ -21738,7 +22236,7 @@
 
 #, c-format
 msgid "could not delete references: %s"
-msgstr "Указателите не може да бъдат изтрити: %s"
+msgstr "Указателите не може да се изтрият: %s"
 
 #, c-format
 msgid "Finished dry-run migration of refs, the result can be found at '%s'\n"
@@ -21766,7 +22264,7 @@
 "„core.preferSymlinkRefs=true“ е отбелязан за изтриване.\n"
 "ПОДСКАЗКА: Ползването на символни връзки за символни указатели е остаряло и\n"
 "ПОДСКАЗКА: ще се преустанови в Git 3.0.  Настройката на Git да прави това\n"
-"ПОДСКАЗКА: по този начин също предстои да бъде премахната.  Може да я "
+"ПОДСКАЗКА: по този начин също предстои да се премахне.  Може да я "
 "премахнете\n"
 "ПОДСКАЗКА: сами още сега с командата:\n"
 "ПОДСКАЗКА:\n"
@@ -21869,7 +22367,7 @@
 
 #, c-format
 msgid "refname %s not found"
-msgstr "името на указателя „%s“ не може да се открие"
+msgstr "името на указателя „%s“ липсва"
 
 #, c-format
 msgid "refname %s is a symbolic ref, copying it is not supported"
@@ -21931,6 +22429,14 @@
 msgstr "няма достъп до „%s“ със следната настройка на „http.pinnedPubkey“: %s"
 
 #, c-format
+msgid "rate limited by '%s', please try again in %ld seconds"
+msgstr "скоростта е ограничена от „%s“, пробвайте отново след %ld секунди"
+
+#, c-format
+msgid "rate limited by '%s', please try again later"
+msgstr "скоростта е ограничена от „%s“, пробвайте по-късно"
+
+#, c-format
 msgid "unable to access '%s': %s"
 msgstr "няма достъп до „%s“: %s"
 
@@ -22218,11 +22724,12 @@
 msgstr "• прескачане на неочаквания локален указател „%s“"
 
 #, c-format
-msgid "Your branch is based on '%s', but the upstream is gone.\n"
-msgstr "Този клон следи „%s“, но следеният клон е изтрит.\n"
-
-msgid "  (use \"git branch --unset-upstream\" to fixup)\n"
-msgstr "  (за да коригирате това, използвайте „git branch --unset-upstream“)\n"
+msgid ""
+"ignoring value '%s' for status.compareBranches, only @{upstream} and @{push} "
+"are supported"
+msgstr ""
+"прескачане на стойността „%s“ за настройката „status.compareBranches“.  "
+"Поддържат се само „@{upstream}“ и „@{push}“"
 
 #, c-format
 msgid "Your branch is up to date with '%s'.\n"
@@ -22274,6 +22781,13 @@
 msgstr "  (слейте отдалечения клон в локалния чрез „git pull“)\n"
 
 #, c-format
+msgid "Your branch is based on '%s', but the upstream is gone.\n"
+msgstr "Този клон следи „%s“, но следеният клон е изтрит.\n"
+
+msgid "  (use \"git branch --unset-upstream\" to fixup)\n"
+msgstr "  (за да коригирате това, използвайте „git branch --unset-upstream“)\n"
+
+#, c-format
 msgid "cannot parse expected object name '%s'"
 msgstr "очакваното име на обект „%s“ не може да се анализира"
 
@@ -22302,7 +22816,7 @@
 
 #, c-format
 msgid "could not remove stale bitmap: %s"
-msgstr "изтриването на остарялата битова маска „%s“ е невъзможно"
+msgstr "невъзможно изтриване на битовата маска с изтекла давност „%s“"
 
 msgid "could not start pack-objects to repack promisor objects"
 msgstr ""
@@ -22356,6 +22870,38 @@
 msgid "replace depth too high for object %s"
 msgstr "дълбочината на замяна е прекалено голяма за обекта: „%s“"
 
+#, c-format
+msgid "'%s' is not a valid commit-ish for %s"
+msgstr "„%s“ не прилича на указател към подаване за %s"
+
+#, c-format
+msgid "argument to %s must be a reference"
+msgstr "аргументът към „%s“ трябва да е указател"
+
+#, c-format
+msgid ""
+"'%s' cannot be used with multiple revision ranges because the ordering would "
+"be ill-defined"
+msgstr ""
+"„%s“ не може да се ползва с множество диапазони на версиите, защото "
+"подредбата не е добре дефинирана"
+
+msgid "need some commits to replay"
+msgstr "необходимо е да има подавания за прилагане отново"
+
+msgid "all positive revisions given must be references"
+msgstr "всички зададени положителни версии трябва да са указатели"
+
+msgid "'--ref' cannot be used with multiple revision ranges"
+msgstr "опцията „--ref“ е несъвместима с множество диапазони на версии"
+
+#, c-format
+msgid "'%s' is not a valid refname"
+msgstr "„%s“ е неправилно име на указател"
+
+msgid "compatibility hash algorithm support requires Rust"
+msgstr "поддръжката на съвместим алгоритъм за контролна сума изисква Rust"
+
 msgid "corrupt MERGE_RR"
 msgstr "повреден „MERGE_RR“ (запис за коригиране на конфликт)"
 
@@ -22430,7 +22976,7 @@
 
 #, c-format
 msgid "failed to find tree of %s"
-msgstr "дървото, сочено от „%s“, не може да се открие"
+msgstr "липсва дървото, сочено от „%s“"
 
 #, c-format
 msgid "unsupported section for hidden refs: %s"
@@ -22636,11 +23182,12 @@
 
 #, c-format
 msgid "could not remove stale scalar.repo '%s'"
-msgstr "остарялото скаларно хранилище (scalar.repo) „%s“ не може да се изтрие"
+msgstr ""
+"не може да се изтрие скаларно хранилище с изтекла давност (scalar.repo) „%s“"
 
 #, c-format
 msgid "removed stale scalar.repo '%s'"
-msgstr "изтриване на остарялото скаларно хранилище (scalar.repo) „%s“"
+msgstr "изтриване на скаларно хранилище (scalar.repo) с изтекла давност „%s“"
 
 #, c-format
 msgid "repository at '%s' has different owner"
@@ -22784,7 +23331,8 @@
 "След коригирането на конфликтите отбележете решаването им чрез:\n"
 "„git add/rm ФАЙЛ_С_КОНФЛИКТ…“ и изпълнете „git rebase --continue“.\n"
 "Ако предпочитате да прескочите тази кръпка, изпълнете „git rebase --skip“.\n"
-"За да откажете пребазирането и да се върнете към първоначалното състояние,\n"
+"За да преустановите пребазирането и да се върнете към първоначалното "
+"състояние,\n"
 "изпълнете „git rebase --abort“."
 
 msgid ""
@@ -22819,7 +23367,8 @@
 "\n"
 "    git cherry-pick --skip\n"
 "\n"
-"За да откажете пребазирането и да се върнете към първоначалното състояние,\n"
+"За да преустановите пребазирането и да се върнете към първоначалното "
+"състояние,\n"
 "изпълнете:\n"
 "\n"
 "    git cherry-pick --abort"
@@ -22844,7 +23393,8 @@
 "\n"
 "    git revert --skip\n"
 "\n"
-"За да откажете пребазирането и да се върнете към първоначалното състояние,\n"
+"За да преустановите пребазирането и да се върнете към първоначалното "
+"състояние,\n"
 "изпълнете:\n"
 "\n"
 "    git revert --abort"
@@ -22863,7 +23413,7 @@
 
 #, c-format
 msgid "your local changes would be overwritten by %s."
-msgstr "локалните промѐни ще бъдат презаписани при %s."
+msgstr "локалните промѐни ще се презапишат при %s."
 
 msgid "commit your changes or stash them to proceed."
 msgstr "подайте или скатайте промѐните, за да продължите"
@@ -23127,10 +23677,6 @@
 msgstr "„%s“ е неправилен етикет"
 
 #, c-format
-msgid "'%s' is not a valid refname"
-msgstr "„%s“ е неправилно име на указател"
-
-#, c-format
 msgid "update-ref requires a fully qualified refname e.g. refs/heads/%s"
 msgstr ""
 "командата „update-ref“ изисква пълно име на указател, напр. „refs/heads/%s“"
@@ -23228,6 +23774,15 @@
 msgid "cannot revert during a cherry-pick."
 msgstr "по време на отбиране не може да се извърши отмяна на подаване."
 
+msgid "trailers file contains empty line"
+msgstr "файлът-епилог съдържа празен ред"
+
+msgid "trailers file is empty"
+msgstr "празен файл-епилог"
+
+msgid "cannot read trailers files"
+msgstr "файловете-епилози не може да се прочетат"
+
 msgid "unusable squash-onto"
 msgstr "подаването, в което другите да се вкарат, не може да се използва"
 
@@ -23571,7 +24126,7 @@
 msgstr "указателят „CHERRY_PICK_HEAD“ не може да се изтрие"
 
 msgid "could not commit staged changes."
-msgstr "промѐните в индекса не може да бъдат подадени."
+msgstr "промѐните в индекса не може да се подадат."
 
 #, c-format
 msgid "%s: can't cherry-pick a %s"
@@ -23730,6 +24285,14 @@
 "\n"
 "    git config --global --add safe.directory %s"
 
+#, c-format
+msgid "error reading '%s'"
+msgstr "грешка при прочитане на „%s“"
+
+#, c-format
+msgid "not a regular file: '%s'"
+msgstr "не е обикновен файл: „%s“"
+
 msgid "Unable to read current working directory"
 msgstr "Текущата работна директория не може да се прочете"
 
@@ -23759,6 +24322,10 @@
 "голото хранилище „%s“ не може да се ползва („safe.bareRepository“ е „%s“)"
 
 #, c-format
+msgid "unknown ref storage format: '%s'"
+msgstr "непознат формат на съхранение на указатели: „%s“"
+
+#, c-format
 msgid ""
 "problem with core.sharedRepository filemode value (0%.3o).\n"
 "The owner of files must always have read and write permissions."
@@ -23803,7 +24370,7 @@
 
 #, c-format
 msgid "not copying templates from '%s': %s"
-msgstr "шаблоните няма да бъдат копирани от „%s“: „%s“"
+msgstr "шаблоните няма да се копират от „%s“: „%s“"
 
 #, c-format
 msgid "invalid initial branch name: '%s'"
@@ -23956,8 +24523,8 @@
 
 msgid "Cannot change unmerged .gitmodules, resolve merge conflicts first"
 msgstr ""
-"Неслетите файлове „.gitmodules“ не може да бъдат променяни.  Първо "
-"коригирайте конфликтите"
+"Неслетите файлове „.gitmodules“ не може да се променят.  Първо коригирайте "
+"конфликтите"
 
 #, c-format
 msgid "Could not find section in .gitmodules where path=%s"
@@ -23987,7 +24554,7 @@
 "Submodule in commit %s at path: '%s' collides with a submodule named the "
 "same. Skipping it."
 msgstr ""
-"Подмодулът при подаване %s на пътя „%s“ е различен от другия модул със "
+"Подмодулът при подаване %s на пътя „%s“ е различен от другия подмодул със "
 "същото име, затова първият се прескача."
 
 #, c-format
@@ -24103,10 +24670,6 @@
 msgstr "името на подмодула „%s“ не може да се намери"
 
 #, c-format
-msgid "refusing to move '%s' into an existing git dir"
-msgstr "„%s“ не може да се премести в съществуваща директория на git"
-
-#, c-format
 msgid ""
 "Migrating git directory of '%s%s' from\n"
 "'%s' to\n"
@@ -24124,6 +24687,33 @@
 msgstr "„ls-tree“ завърши с неочакван изходен код: %d"
 
 #, c-format
+msgid ""
+"the 'submodule.%s.gitdir' config does not exist for module '%s'. Please "
+"ensure it is set, for example by running something like: 'git config "
+"submodule.%s.gitdir .git/modules/%s'. For details see the "
+"extensions.submodulePathConfig documentation."
+msgstr ""
+"настройката „submodule.%s.gitdir“ не съществува за подмодула „%s“.  Уверете "
+"се, че е зададена, напр. като изпълнете командата:\n"
+"\n"
+"    git config submodule.%s.gitdir .git/modules/%s\n"
+"\n"
+"За подробности вижте документацията на „extensions.submodulePathConfig“."
+
+msgid ""
+"enabling extensions.submodulePathConfig might fix the following error, if "
+"it's not already enabled."
+msgstr ""
+"ако не е включена настройката „extensions.submodulePathConfig“, пробвайте да "
+"я включите, за да коригирате следващата грешка."
+
+#, c-format
+msgid "refusing to create/use '%s' in another submodule's  git dir."
+msgstr ""
+"„%s“ не може нито да се създаде, нито да се ползва в директорията на git на "
+"друг подмодул"
+
+#, c-format
 msgid "failed to lstat '%s'"
 msgstr "не може да се получи информация чрез „lstat“ за „%s“"
 
@@ -24179,6 +24769,10 @@
 msgid "too many commits marked reachable"
 msgstr "прекалено много подавания са отбелязани като достижими"
 
+#, c-format
+msgid "could not find MIDX with checksum %s"
+msgstr "липсва файл с индекс за множество пакети с контролна сума „%s“"
+
 msgid "could not determine MIDX preferred pack"
 msgstr ""
 "предпочитаният пакет за файла с индекса за множество пакети не може да се "
@@ -24272,6 +24866,28 @@
 msgid "empty trailer token in trailer '%.*s'"
 msgstr "празна завършваща лексема в епилога „%.*s“"
 
+msgid "empty --trailer argument"
+msgstr "празен аргумент към „--trailer“"
+
+#, c-format
+msgid "invalid trailer '%s': missing key before separator"
+msgstr "неправилен епилог „%s“: липсва ключ пред разделителя"
+
+#, c-format
+msgid "could not stat %s"
+msgstr "Не може да се получи информация чрез „stat“ за „%s“"
+
+#, c-format
+msgid "file %s is not a regular file"
+msgstr "„%s“ не е обикновен файл"
+
+#, c-format
+msgid "file %s is not writable by user"
+msgstr "„%s“: няма права̀ за записване на файла"
+
+msgid "could not write to temporary file"
+msgstr "не може да се пише във временен файл"
+
 msgid "full write to remote helper failed"
 msgstr "неуспешен пълен запис към насрещната помощна програма"
 
@@ -24877,13 +25493,13 @@
 msgstr "неправилен път"
 
 msgid "unable to locate repository; .git is not a file"
-msgstr "не може да се открие хранилище: „.git“ не е файл"
+msgstr "липсва хранилище: „.git“ не е файл"
 
 msgid "unable to locate repository; .git file does not reference a repository"
-msgstr "не може да се открие хранилище: файлът „.git“ не сочи към хранилище"
+msgstr "липсва хранилище: файлът „.git“ не сочи към хранилище"
 
 msgid "unable to locate repository; .git file broken"
-msgstr "не може да се открие хранилище: „.git“ е повреден"
+msgstr "липсва хранилище: „.git“ е повреден"
 
 msgid "gitdir unreadable"
 msgstr "директорията „gitdir“ не може да се прочете"
@@ -24997,7 +25613,7 @@
 "  (използвайте „git rm ФАЙЛ…“, за да укажете разрешаването на конфликта)"
 
 msgid "Changes to be committed:"
-msgstr "Промѐни, които ще бъдат подадени:"
+msgstr "Промѐни, които ще се подадат:"
 
 msgid "Changes not staged for commit:"
 msgstr "Промѐни, които не са в индекса за подаване:"
@@ -25310,7 +25926,7 @@
 msgstr "Указателят „HEAD“ не е свързан и е отделѐн от "
 
 msgid "Not currently on any branch."
-msgstr "Извън всички клони."
+msgstr "Извън всички кло̀ни."
 
 msgid "Initial commit"
 msgstr "Първоначално подаване"
@@ -25435,19 +26051,19 @@
 
 #, sh-format
 msgid "Unable to find common commit with $pretty_name"
-msgstr "Не може да се открие общо подаване с „$pretty_name“"
+msgstr "Липсва общо подаване с $pretty_name"
 
 #, sh-format
 msgid "Already up to date with $pretty_name"
-msgstr "Вече е обновено до „$pretty_name“"
+msgstr "Вече е обновено с $pretty_name"
 
 #, sh-format
 msgid "Fast-forwarding to: $pretty_name"
-msgstr "Превъртане до „$pretty_name“"
+msgstr "Превъртане до: $pretty_name"
 
 #, sh-format
 msgid "Trying simple merge with $pretty_name"
-msgstr "Опит за просто сливане с „$pretty_name“"
+msgstr "Опит за просто сливане с $pretty_name"
 
 msgid "Simple merge did not work, trying automatic merge."
 msgstr "Простото сливане не сработи, пробва се автоматично сливане."
@@ -25468,7 +26084,7 @@
 
 msgid "Cannot rewrite branches: You have unstaged changes."
 msgstr ""
-"Не може да презапишете клоните, защото има промѐни, които не са в индекса."
+"Не може да презапишете кло̀ните, защото има промѐни, които не са в индекса."
 
 #, sh-format
 msgid "Cannot $action: You have unstaged changes."
@@ -25610,7 +26226,7 @@
 "\n"
 "Clear the body content if you don't wish to send a summary.\n"
 msgstr ""
-"Редовете започващи с „GIT:“ ще бъдат прескачани.\n"
+"Редовете започващи с „GIT:“ ще се прескачат.\n"
 "Добре е да включите статистика на разликите или някаква таблица на "
 "съдържанието\n"
 "на подготвяната кръпка.\n"
@@ -25640,10 +26256,14 @@
 "Следните файлове са 8 битови, но не са с обявена заглавна част „Content-"
 "Transfer-Encoding“.\n"
 
-msgid "Which 8bit encoding should I declare [UTF-8]? "
+msgid "Declare which 8bit encoding to use [default: UTF-8]? "
 msgstr "Кое 8 битово кодиране се ползва [стандартно: UTF-8]? "
 
 #, perl-format
+msgid "'%s' does not appear to be a valid charset name. Use it anyway [y/N]? "
+msgstr "„%s“ не изглежда име на кодиране.  Да се ползва ли въпреки това? [y/N]"
+
+#, perl-format
 msgid ""
 "Refusing to send because the patch\n"
 "\t%s\n"
@@ -25681,7 +26301,11 @@
 
 #, perl-format
 msgid "CA path \"%s\" does not exist"
-msgstr "Пътят към сертификат „%s“ не съществува."
+msgstr "Пътят към сертификат „%s“ не съществува"
+
+#, perl-format
+msgid "Only client key \"%s\" specified"
+msgstr "Указан е само клиентският ключ „%s“"
 
 msgid ""
 "    The Cc list above has been expanded by additional\n"
diff --git a/po/ca.po b/po/ca.po
index 8dc21f3..0275d51 100644
--- a/po/ca.po
+++ b/po/ca.po
@@ -80,8 +80,8 @@
 msgstr ""
 "Project-Id-Version: Git\n"
 "Report-Msgid-Bugs-To: Git Mailing List <git@vger.kernel.org>\n"
-"POT-Creation-Date: 2025-08-12 20:20-0400\n"
-"PO-Revision-Date: 2025-08-15 22:35+0200\n"
+"POT-Creation-Date: 2026-04-15 07:38+0800\n"
+"PO-Revision-Date: 2026-04-10 16:52+0200\n"
 "Last-Translator: Mikel Forcada <mikel.forcada@gmail.com>\n"
 "Language-Team: Catalan\n"
 "Language: ca\n"
@@ -93,11 +93,6 @@
 
 #: add-interactive.c
 #, c-format
-msgid "%s cannot be negative"
-msgstr "%s no pot ser negatiu"
-
-#: add-interactive.c
-#, c-format
 msgid "Huh (%s)?"
 msgstr "Perdó (%s)?"
 
@@ -298,23 +293,23 @@
 
 #: add-patch.c
 #, c-format
-msgid "Stage mode change [y,n,q,a,d%s,?]? "
-msgstr "Canvia el mode de «stage» [y,n,q,a,d%s,?]? "
+msgid "Stage mode change%s [y,n,q,a,d%s,?]? "
+msgstr "Canvia el mode de «stage»%s [y,n,q,a,d%s,?]? "
 
 #: add-patch.c
 #, c-format
-msgid "Stage deletion [y,n,q,a,d%s,?]? "
-msgstr "Suprimeix «stage» [y,n,q,a,d%s,?]? "
+msgid "Stage deletion%s [y,n,q,a,d%s,?]? "
+msgstr "Fer «stage» de la supressió%s [y,n,q,a,d%s,?]? "
 
 #: add-patch.c
 #, c-format
-msgid "Stage addition [y,n,q,a,d%s,?]? "
-msgstr "Afegeix a «stage» [y,n,q,a,d%s,?]? "
+msgid "Stage addition%s [y,n,q,a,d%s,?]? "
+msgstr "Fer «stage» de l'addició%s [y,n,q,a,d%s,?]? "
 
 #: add-patch.c
 #, c-format
-msgid "Stage this hunk [y,n,q,a,d%s,?]? "
-msgstr "Fer un «stage» d'aquest tros [y,n,q,a,d%s,?]? "
+msgid "Stage this hunk%s [y,n,q,a,d%s,?]? "
+msgstr "Fer un «stage» d'aquest tros%s [y,n,q,a,d%s,?]? "
 
 #: add-patch.c
 msgid ""
@@ -341,23 +336,23 @@
 
 #: add-patch.c
 #, c-format
-msgid "Stash mode change [y,n,q,a,d%s,?]? "
-msgstr "Canvia el mode de «stash» [y,n,q,a,d%s,?]? "
+msgid "Stash mode change%s [y,n,q,a,d%s,?]? "
+msgstr "Canvia el mode de «stash»%s [y,n,q,a,d%s,?]? "
 
 #: add-patch.c
 #, c-format
-msgid "Stash deletion [y,n,q,a,d%s,?]? "
-msgstr "Suprimeix «stash» [y,n,q,a,d%s,?]? "
+msgid "Stash deletion%s [y,n,q,a,d%s,?]? "
+msgstr "Fer «stash»  de la supressió%s [y,n,q,a,d%s,?]? "
 
 #: add-patch.c
 #, c-format
-msgid "Stash addition [y,n,q,a,d%s,?]? "
-msgstr "Afegeix a «stash» [y,n,q,a,d%s,?]? "
+msgid "Stash addition%s [y,n,q,a,d%s,?]? "
+msgstr "Fer «stash» de l'addició%s [y,n,q,a,d%s,?]? "
 
 #: add-patch.c
 #, c-format
-msgid "Stash this hunk [y,n,q,a,d%s,?]? "
-msgstr "Fer un «stash» d'aquest tros [y,n,q,a,d%s,?]? "
+msgid "Stash this hunk%s [y,n,q,a,d%s,?]? "
+msgstr "Fer un «stash» d'aquest tros%s [y,n,q,a,d%s,?]? "
 
 #: add-patch.c
 msgid ""
@@ -384,23 +379,23 @@
 
 #: add-patch.c
 #, c-format
-msgid "Unstage mode change [y,n,q,a,d%s,?]? "
-msgstr "Canvia el mode de «unstage» [y,n,q,a,d%s,?]? "
+msgid "Unstage mode change%s [y,n,q,a,d%s,?]? "
+msgstr "Canvia el mode de «unstage»%s [y,n,q,a,d%s,?]? "
 
 #: add-patch.c
 #, c-format
-msgid "Unstage deletion [y,n,q,a,d%s,?]? "
-msgstr "Suprimeix «Unstage» [y,n,q,a,d%s,?]? "
+msgid "Unstage deletion%s [y,n,q,a,d%s,?]? "
+msgstr "Fer «Unstage» de la supressió%s [y,n,q,a,d%s,?]? "
 
 #: add-patch.c
 #, c-format
-msgid "Unstage addition [y,n,q,a,d%s,?]? "
-msgstr "Afegeix a «unstage» [y,n,q,a,d%s,?]? "
+msgid "Unstage addition%s [y,n,q,a,d%s,?]? "
+msgstr "Fer un «unstage» de l'addició%s [y,n,q,a,d%s,?]? "
 
 #: add-patch.c
 #, c-format
-msgid "Unstage this hunk [y,n,q,a,d%s,?]? "
-msgstr "Fer un «unstage» d'aquest tros [y,n,q,a,d%s,?]? "
+msgid "Unstage this hunk%s [y,n,q,a,d%s,?]? "
+msgstr "Fer un «unstage» d'aquest tros%s [y,n,q,a,d%s,?]? "
 
 #: add-patch.c
 msgid ""
@@ -426,23 +421,23 @@
 
 #: add-patch.c
 #, c-format
-msgid "Apply mode change to index [y,n,q,a,d%s,?]? "
-msgstr "Aplica el canvi de mode a l'índex [y,n,q,a,d%s,?]? "
+msgid "Apply mode change to index%s [y,n,q,a,d%s,?]? "
+msgstr "Aplica el canvi de mode a l'índex%s [y,n,q,a,d%s,?]? "
 
 #: add-patch.c
 #, c-format
-msgid "Apply deletion to index [y,n,q,a,d%s,?]? "
-msgstr "Aplica la supressió a l'índex [y,n,q,a,d%s,?]? "
+msgid "Apply deletion to index%s [y,n,q,a,d%s,?]? "
+msgstr "Aplica la supressió a l'índex%s [y,n,q,a,d%s,?]? "
 
 #: add-patch.c
 #, c-format
-msgid "Apply addition to index [y,n,q,a,d%s,?]? "
-msgstr "Aplica l'addició a l'índex [y,n,q,a,d%s,?]? "
+msgid "Apply addition to index%s [y,n,q,a,d%s,?]? "
+msgstr "Aplica l'addició a l'índex%s [y,n,q,a,d%s,?]? "
 
 #: add-patch.c
 #, c-format
-msgid "Apply this hunk to index [y,n,q,a,d%s,?]? "
-msgstr "Aplica aquest tros a l'índex [y,n,q,a,d%s,?]? "
+msgid "Apply this hunk to index%s [y,n,q,a,d%s,?]? "
+msgstr "Aplica aquest tros a l'índex%s [y,n,q,a,d%s,?]? "
 
 #: add-patch.c
 msgid ""
@@ -468,23 +463,24 @@
 
 #: add-patch.c
 #, c-format
-msgid "Discard mode change from worktree [y,n,q,a,d%s,?]? "
-msgstr "Descarta el canvi de mode de l'arbre de treball [y,n,q,a,d%s,?]? "
+msgid "Discard mode change from worktree%s [y,n,q,a,d%s,?]? "
+msgstr "Descarta el canvi de mode de l'arbre de treball%s [y,n,q,a,d%s,?]? "
 
 #: add-patch.c
 #, c-format
-msgid "Discard deletion from worktree [y,n,q,a,d%s,?]? "
-msgstr "Descarta suprimir de l'arbre de treball [y,n,q,a,d%s,?]? "
+msgid "Discard deletion from worktree%s [y,n,q,a,d%s,?]? "
+msgstr "Descarta suprimir de l'arbre de treball%s [y,n,q,a,d%s,?]? "
 
 #: add-patch.c
 #, c-format
-msgid "Discard addition from worktree [y,n,q,a,d%s,?]? "
-msgstr "Descarta l'addició de l'arbre de treball [y,n,q,a,d%s,?]? "
+msgid "Discard addition from worktree%s [y,n,q,a,d%s,?]? "
+msgstr ""
+"Descarta l'addició de l'índex i de l'arbre de treball%s [y,n,q,a,d%s,?]? "
 
 #: add-patch.c
 #, c-format
-msgid "Discard this hunk from worktree [y,n,q,a,d%s,?]? "
-msgstr "Descarta aquest tros de l'arbre de treball [y,n,q,a,d%s,?]? "
+msgid "Discard this hunk from worktree%s [y,n,q,a,d%s,?]? "
+msgstr "Descarta aquest tros de l'arbre de treball%s [y,n,q,a,d%s,?]? "
 
 #: add-patch.c
 msgid ""
@@ -510,27 +506,28 @@
 
 #: add-patch.c
 #, c-format
-msgid "Discard mode change from index and worktree [y,n,q,a,d%s,?]? "
+msgid "Discard mode change from index and worktree%s [y,n,q,a,d%s,?]? "
 msgstr ""
-"Descarta el canvi de mode de l'índex i de l'arbre de treball [y,n,q,a,d"
-"%s,?]? "
+"Descarta el canvi de mode de l'índex i de l'arbre de treball%s "
+"[y,n,q,a,d%s,?]? "
 
 #: add-patch.c
 #, c-format
-msgid "Discard deletion from index and worktree [y,n,q,a,d%s,?]? "
-msgstr "Descarta suprimir de l'índex i de l'arbre de treball [y,n,q,a,d%s,?]? "
+msgid "Discard deletion from index and worktree%s [y,n,q,a,d%s,?]? "
+msgstr ""
+"Descarta suprimir de l'índex i de l'arbre de treball%s [y,n,q,a,d%s,?]? "
 
 #: add-patch.c
 #, c-format
-msgid "Discard addition from index and worktree [y,n,q,a,d%s,?]? "
+msgid "Discard addition from index and worktree%s [y,n,q,a,d%s,?]? "
 msgstr ""
-"Descarta l'addició de l'índex i de l'arbre de treball [y,n,q,a,d%s,?]? "
+"Descarta l'addició de l'índex i de l'arbre de treball%s [y,n,q,a,d%s,?]? "
 
 #: add-patch.c
 #, c-format
-msgid "Discard this hunk from index and worktree [y,n,q,a,d%s,?]? "
+msgid "Discard this hunk from index and worktree%s [y,n,q,a,d%s,?]? "
 msgstr ""
-"Descarta aquest tros de l'índex i de l'arbre de treball [y,n,q,a,d%s,?]? "
+"Descarta aquest tros de l'índex i de l'arbre de treball%s [y,n,q,a,d%s,?]? "
 
 #: add-patch.c
 msgid ""
@@ -548,24 +545,26 @@
 
 #: add-patch.c
 #, c-format
-msgid "Apply mode change to index and worktree [y,n,q,a,d%s,?]? "
+msgid "Apply mode change to index and worktree%s [y,n,q,a,d%s,?]? "
 msgstr ""
-"Aplica el canvi de mode a l'índex i a l'arbre de treball [y,n,q,a,d%s,?]? "
+"Aplica el canvi de mode a l'índex i a l'arbre de treball%s [y,n,q,a,d%s,?]? "
 
 #: add-patch.c
 #, c-format
-msgid "Apply deletion to index and worktree [y,n,q,a,d%s,?]? "
-msgstr "Aplica la supressió a l'índex i a l'arbre de treball [y,n,q,a,d%s,?]? "
+msgid "Apply deletion to index and worktree%s [y,n,q,a,d%s,?]? "
+msgstr ""
+"Aplica la supressió a l'índex i a l'arbre de treball%s [y,n,q,a,d%s,?]? "
 
 #: add-patch.c
 #, c-format
-msgid "Apply addition to index and worktree [y,n,q,a,d%s,?]? "
-msgstr "Aplica l'addició a l'índex i a l'arbre de treball [y,n,q,a,d%s,?]? "
+msgid "Apply addition to index and worktree%s [y,n,q,a,d%s,?]? "
+msgstr "Aplica l'addició a l'índex i a l'arbre de treball%s [y,n,q,a,d%s,?]? "
 
 #: add-patch.c
 #, c-format
-msgid "Apply this hunk to index and worktree [y,n,q,a,d%s,?]? "
-msgstr "Aplica aquest tros a l'índex i a l'arbre de treball [y,n,q,a,d%s,?]? "
+msgid "Apply this hunk to index and worktree%s [y,n,q,a,d%s,?]? "
+msgstr ""
+"Aplica aquest tros a l'índex i a l'arbre de treball%s [y,n,q,a,d%s,?]? "
 
 #: add-patch.c
 msgid ""
@@ -583,23 +582,23 @@
 
 #: add-patch.c
 #, c-format
-msgid "Apply mode change to worktree [y,n,q,a,d%s,?]? "
-msgstr "Aplica el canvi de mode a l'arbre de treball [y,n,q,a,d%s,?]? "
+msgid "Apply mode change to worktree%s [y,n,q,a,d%s,?]? "
+msgstr "Aplica el canvi de mode a l'arbre de treball%s [y,n,q,a,d%s,?]? "
 
 #: add-patch.c
 #, c-format
-msgid "Apply deletion to worktree [y,n,q,a,d%s,?]? "
-msgstr "Aplica la supressió a l'arbre de treball [y,n,q,a,d%s,?]? "
+msgid "Apply deletion to worktree%s [y,n,q,a,d%s,?]? "
+msgstr "Aplica la supressió a l'arbre de treball%s [y,n,q,a,d%s,?]? "
 
 #: add-patch.c
 #, c-format
-msgid "Apply addition to worktree [y,n,q,a,d%s,?]? "
-msgstr "Aplica l'addició a l'arbre de treball [y,n,q,a,d%s,?]? "
+msgid "Apply addition to worktree%s [y,n,q,a,d%s,?]? "
+msgstr "Aplica l'addició a l'arbre de treball%s [y,n,q,a,d%s,?]? "
 
 #: add-patch.c
 #, c-format
-msgid "Apply this hunk to worktree [y,n,q,a,d%s,?]? "
-msgstr "Aplica aquest tros a l'arbre de treball [y,n,q,a,d%s,?]? "
+msgid "Apply this hunk to worktree%s [y,n,q,a,d%s,?]? "
+msgstr "Aplica aquest tros a l'arbre de treball%s [y,n,q,a,d%s,?]? "
 
 #: add-patch.c
 msgid ""
@@ -617,6 +616,11 @@
 
 #: add-patch.c
 #, c-format
+msgid "%s cannot be negative"
+msgstr "%s no pot ser negatiu"
+
+#: add-patch.c
+#, c-format
 msgid "could not parse hunk header '%.*s'"
 msgstr "no s'ha pogut analitzar la capçalera del tros «%.*s»"
 
@@ -729,28 +733,49 @@
 msgstr "No s'ha aplicat res.\n"
 
 #: add-patch.c
+#, c-format
 msgid ""
-"j - leave this hunk undecided, see next undecided hunk\n"
-"J - leave this hunk undecided, see next hunk\n"
-"k - leave this hunk undecided, see previous undecided hunk\n"
-"K - leave this hunk undecided, see previous hunk\n"
+"j - go to the next undecided hunk, roll over at the bottom\n"
+"J - go to the next hunk, roll over at the bottom\n"
+"k - go to the previous undecided hunk, roll over at the top\n"
+"K - go to the previous hunk, roll over at the top\n"
 "g - select a hunk to go to\n"
 "/ - search for a hunk matching the given regex\n"
 "s - split the current hunk into smaller hunks\n"
 "e - manually edit the current hunk\n"
-"p - print the current hunk, 'P' to use the pager\n"
+"p - print the current hunk\n"
+"P - print the current hunk using the pager\n"
+"> - go to the next file, roll over at the bottom\n"
+"< - go to the previous file, roll over at the top\n"
 "? - print help\n"
+"HUNKS SUMMARY - Hunks: %d, USE: %d, SKIP: %d\n"
 msgstr ""
-"j - deixa aquest tros sense decidir, veges el tros següent no decidit \n"
-"J - deixa aquest tros sense decidir, veges el tros següent\n"
-"k - deixa aquest tros sense decidir, veges el tros anterior no decidit ºn\n"
-"K - deixa aquest tros sense decidir, veges el tros anterior\n"
+"j - vés al tros següent sense decidir, torna al principi si ets al final\n"
+"J -vés al tros següent, torna al principi si ets al final\n"
+"k - vés al tros anterior sense decidir, i torna al final si ets al principi\n"
+"K - vés al tros anterior, i torna al final si ets al principi\n"
 "g - selecciona un tros a on anar\n"
 "/ - cerca un tros que concorde amb l'expressió regular donada\n"
 "s - divideix el tros actual en trossos més menuts\n"
 "e - edita manualment el tros actual\n"
-"p - imprimeix el tros actual, «P» per a usar el paginador\n"
+"p - imprimeix el tros actual, \n"
+"P - imprimeix el tros actual, usant el paginador\n"
+"> - vés al fitxer següent, torna al principi si ets al final\n"
+"< - vés al fitxer anterior, i torna al final si ets al principi\n"
 "? - imprimeix l'ajuda\n"
+"RESUM DE TROSSOS - Trossos %d, USANT %d, SALTANT %d\n"
+
+#: add-patch.c
+msgid "'git apply' failed"
+msgstr "«git apply» ha fallat"
+
+#: add-patch.c
+msgid " (was: y)"
+msgstr " (era: y)"
+
+#: add-patch.c
+msgid " (was: n)"
+msgstr " (era: n)"
 
 #: add-patch.c
 #, c-format
@@ -758,12 +783,20 @@
 msgstr "S'espera una lletra, s'ha obtingut «%s»"
 
 #: add-patch.c
-msgid "No previous hunk"
-msgstr "Sense tros previ"
+msgid "No next file"
+msgstr "No hi ha fitxer següent"
 
 #: add-patch.c
-msgid "No next hunk"
-msgstr "No hi ha tros següent"
+msgid "No previous file"
+msgstr "Cap fitxer anterior"
+
+#: add-patch.c
+msgid "No other hunk"
+msgstr "No hi ha altres trossos"
+
+#: add-patch.c
+msgid "No other undecided hunk"
+msgstr "No hi ha més trossos no decidits"
 
 #: add-patch.c
 msgid "No other hunks to goto"
@@ -825,10 +858,6 @@
 msgstr "Ordre desconeguda: «%s» (useu «?» per a obtenir ajuda)"
 
 #: add-patch.c
-msgid "'git apply' failed"
-msgstr "«git apply» ha fallat"
-
-#: add-patch.c
 msgid "No changes."
 msgstr "No hi ha canvis."
 
@@ -836,6 +865,30 @@
 msgid "Only binary files changed."
 msgstr "Només han canviat els fitxers binaris."
 
+#: add-patch.c
+#, c-format
+msgid "Stage mode change [y,n,q,a,d%s,?]? "
+msgstr "Canvia el mode de «stage» [y,n,q,a,d%s,?]? "
+
+#: add-patch.c
+#, c-format
+msgid "Stage deletion [y,n,q,a,d%s,?]? "
+msgstr "Fer «stage» de la supresió [y,n,q,a,d%s,?]? "
+
+#: add-patch.c
+#, c-format
+msgid "Stage addition [y,n,q,a,d%s,?]? "
+msgstr "Fer «stage» de l'addició [y,n,q,a,d%s,?]? "
+
+#: add-patch.c
+#, c-format
+msgid "Stage this hunk [y,n,q,a,d%s,?]? "
+msgstr "Fer un «stage» d'aquest tros [y,n,q,a,d%s,?]? "
+
+#: add-patch.c
+msgid "Revision does not refer to a commit"
+msgstr "La revisió no fa referència a una comissió"
+
 #: advice.c
 #, c-format
 msgid ""
@@ -1013,7 +1066,8 @@
 msgstr "cometes no tancades"
 
 #: alias.c builtin/cat-file.c builtin/notes.c builtin/prune-packed.c
-#: builtin/receive-pack.c builtin/refs.c builtin/tag.c t/helper/test-pkt-line.c
+#: builtin/receive-pack.c builtin/refs.c builtin/repo.c builtin/tag.c
+#: t/helper/test-pkt-line.c
 msgid "too many arguments"
 msgstr "hi ha massa arguments"
 
@@ -1033,9 +1087,9 @@
 #: builtin/fast-export.c builtin/fetch.c builtin/help.c builtin/index-pack.c
 #: builtin/init-db.c builtin/log.c builtin/ls-files.c builtin/merge-base.c
 #: builtin/merge-tree.c builtin/merge.c builtin/rebase.c builtin/repack.c
-#: builtin/replay.c builtin/reset.c builtin/rev-parse.c builtin/show-branch.c
-#: builtin/stash.c builtin/submodule--helper.c builtin/tag.c builtin/worktree.c
-#: parse-options.c range-diff.c revision.c
+#: builtin/reset.c builtin/rev-parse.c builtin/show-branch.c builtin/stash.c
+#: builtin/submodule--helper.c builtin/tag.c builtin/worktree.c parse-options.c
+#: range-diff.c revision.c
 #, c-format
 msgid "options '%s' and '%s' cannot be used together"
 msgstr "les opcions «%s» i «%s» no es poden usar juntes"
@@ -1065,8 +1119,14 @@
 
 #: apply.c
 #, c-format
-msgid "unable to find filename in patch at line %d"
-msgstr "no s'ha pogut trobar el nom de fitxer en el pedaç a la línia %d"
+msgid "unable to find filename in patch at %s:%d"
+msgstr "no s'ha pogut trobar el nom de fitxer en el pedaç en %s:%d"
+
+#: apply.c
+#, c-format
+msgid "git apply: bad git-diff - expected /dev/null, got %s at %s:%d"
+msgstr ""
+"git apply: git-diff incorrecte - s'esperava /dev/null, s'ha rebut %s en %s:%d"
 
 #: apply.c
 #, c-format
@@ -1077,6 +1137,18 @@
 
 #: apply.c
 #, c-format
+msgid "git apply: bad git-diff - inconsistent new filename at %s:%d"
+msgstr ""
+"git apply: git-diff incorrecte - nom de fitxer nou inconsistent en %s:%d"
+
+#: apply.c
+#, c-format
+msgid "git apply: bad git-diff - inconsistent old filename at %s:%d"
+msgstr ""
+"git apply: git-diff incorrecte - nom de fitxer antic inconsistent en %s:%d"
+
+#: apply.c
+#, c-format
 msgid "git apply: bad git-diff - inconsistent new filename on line %d"
 msgstr ""
 "git apply: git-diff incorrecte - nom de fitxer nou inconsistent en la línia "
@@ -1091,11 +1163,21 @@
 
 #: apply.c
 #, c-format
+msgid "git apply: bad git-diff - expected /dev/null at %s:%d"
+msgstr "git apply: git-diff incorrecte - s'esperava /dev/null en %s:%d"
+
+#: apply.c
+#, c-format
 msgid "git apply: bad git-diff - expected /dev/null on line %d"
 msgstr "git apply: git-diff incorrecte - s'esperava /dev/null en la línia %d"
 
 #: apply.c
 #, c-format
+msgid "invalid mode at %s:%d: %s"
+msgstr "mode no vàlid en %s:%d: %s"
+
+#: apply.c
+#, c-format
 msgid "invalid mode on line %d: %s"
 msgstr "mode no vàlid en la línia %d: %s"
 
@@ -1108,6 +1190,21 @@
 #, c-format
 msgid ""
 "git diff header lacks filename information when removing %d leading pathname "
+"component at %s:%d"
+msgid_plural ""
+"git diff header lacks filename information when removing %d leading pathname "
+"components at %s:%d"
+msgstr[0] ""
+"a la capçalera de git diff li manca informació de nom de fitxer en eliminar "
+"%d component de nom de camí inicial en %s:%d"
+msgstr[1] ""
+"a la capçalera de git diff li manca informació de nom de fitxer en eliminar "
+"%d components de nom de camí inicial en %s:%d"
+
+#: apply.c
+#, c-format
+msgid ""
+"git diff header lacks filename information when removing %d leading pathname "
 "component (line %d)"
 msgid_plural ""
 "git diff header lacks filename information when removing %d leading pathname "
@@ -1121,6 +1218,12 @@
 
 #: apply.c
 #, c-format
+msgid "git diff header lacks filename information at %s:%d"
+msgstr ""
+"a la capçalera de git diff hi manca informació de nom de fitxer en %s:%d"
+
+#: apply.c
+#, c-format
 msgid "git diff header lacks filename information (line %d)"
 msgstr ""
 "a la capçalera de git diff li manca informació de nom de fitxer (línia %d)"
@@ -1132,8 +1235,8 @@
 
 #: apply.c
 #, c-format
-msgid "patch fragment without header at line %d: %.*s"
-msgstr "fragment de pedaç sense capçalera a la línia %d: %.*s"
+msgid "patch fragment without header at %s:%d: %.*s"
+msgstr "fragment de pedaç sense capçalera en %s:%d: %.*s"
 
 #: apply.c
 msgid "new file depends on old contents"
@@ -1145,8 +1248,8 @@
 
 #: apply.c
 #, c-format
-msgid "corrupt patch at line %d"
-msgstr "pedaç malmès a la línia %d"
+msgid "corrupt patch at %s:%d"
+msgstr "pedaç malmès en %s:%d"
 
 #: apply.c
 #, c-format
@@ -1165,18 +1268,18 @@
 
 #: apply.c
 #, c-format
-msgid "corrupt binary patch at line %d: %.*s"
-msgstr "pedaç binari malmès a la línia %d: %.*s"
+msgid "corrupt binary patch at %s:%d: %.*s"
+msgstr "pedaç binari malmès en %s:%d: %.*s"
 
 #: apply.c
 #, c-format
-msgid "unrecognized binary patch at line %d"
-msgstr "pedaç binari no reconegut a la línia %d"
+msgid "unrecognized binary patch at %s:%d"
+msgstr "pedaç binari no reconegut en %s:%d"
 
 #: apply.c
 #, c-format
-msgid "patch with only garbage at line %d"
-msgstr "pedaç amb només escombraries a la línia %d"
+msgid "patch with only garbage at %s:%d"
+msgstr "pedaç amb només escombraries en %s:%d"
 
 #: apply.c
 #, c-format
@@ -1344,7 +1447,7 @@
 msgid "%s has type %o, expected %o"
 msgstr "%s és del tipus %o, s'esperava %o"
 
-#: apply.c read-cache.c
+#: apply.c builtin/fast-import.c read-cache.c
 #, c-format
 msgid "invalid path '%s'"
 msgstr "camí no vàlid: «%s»"
@@ -1501,6 +1604,16 @@
 
 #: apply.c
 #, c-format
+msgid "option -p expects a non-negative integer, got '%s'"
+msgstr "l'opció -p espera un enter no negatiu, però ha rebut «%s»"
+
+#: apply.c
+#, c-format
+msgid "unable to normalize directory: '%s'"
+msgstr "no s'ha pogut normalitzar el directori: %s"
+
+#: apply.c
+#, c-format
 msgid "can't open patch '%s': %s"
 msgstr "no es pot obrir el pedaç «%s»: %s"
 
@@ -1732,7 +1845,7 @@
 msgid "git archive --remote <repo> [--exec <cmd>] --list"
 msgstr "git archive --remote <repositori> [--exec <ordre>] --list"
 
-#: archive.c builtin/gc.c builtin/notes.c builtin/tag.c
+#: archive.c builtin/fast-import.c builtin/gc.c builtin/notes.c builtin/tag.c
 #, c-format
 msgid "cannot read '%s'"
 msgstr "no es pot llegir «%s»"
@@ -2094,9 +2207,9 @@
 msgstr ""
 "--reverse i --first-parent junts requereixen una última comissió especificada"
 
-#: blame.c builtin/bisect.c builtin/commit.c builtin/log.c builtin/merge.c
-#: builtin/pack-objects.c builtin/shortlog.c midx-write.c pack-bitmap.c
-#: remote.c sequencer.c submodule.c
+#: blame.c builtin/bisect.c builtin/commit.c builtin/fast-export.c
+#: builtin/log.c builtin/merge.c builtin/pack-objects.c builtin/shortlog.c
+#: midx-write.c pack-bitmap.c remote.c sequencer.c submodule.c
 msgid "revision walk setup failed"
 msgstr "la configuració del recorregut de revisions ha fallat"
 
@@ -2224,8 +2337,8 @@
 msgstr "«%s» no és un nom de branca vàlid"
 
 #: branch.c builtin/branch.c
-msgid "See `man git check-ref-format`"
-msgstr "Vegeu `man git check-ref-format`"
+msgid "See 'git help check-ref-format'"
+msgstr "Vegeu «git help check-ref-format»"
 
 #: branch.c
 #, c-format
@@ -2353,7 +2466,7 @@
 
 #: builtin/add.c builtin/clean.c builtin/fetch.c builtin/mv.c
 #: builtin/prune-packed.c builtin/pull.c builtin/push.c builtin/remote.c
-#: builtin/rm.c builtin/send-pack.c
+#: builtin/rm.c builtin/send-pack.c builtin/sparse-checkout.c
 msgid "dry run"
 msgstr "fes una prova"
 
@@ -2371,6 +2484,12 @@
 msgid "select hunks interactively"
 msgstr "selecciona els trossos interactivament"
 
+#: builtin/add.c builtin/checkout.c builtin/reset.c builtin/stash.c
+msgid "auto advance to the next file when selecting hunks interactively"
+msgstr ""
+"avançar automàticament al fitxer següent quan se seleccionen trossos "
+"interactivament"
+
 #: builtin/add.c
 msgid "edit current diff and apply"
 msgstr "edita la diferència actual i aplica-la"
@@ -2503,7 +2622,7 @@
 msgstr "fitxer d'índex malmès"
 
 #: builtin/add.c builtin/am.c builtin/checkout.c builtin/clone.c
-#: builtin/commit.c builtin/stash.c merge.c rerere.c
+#: builtin/commit.c builtin/history.c builtin/stash.c merge.c rerere.c
 msgid "unable to write new index file"
 msgstr "no s'ha pogut escriure un fitxer d'índex nou"
 
@@ -2513,13 +2632,13 @@
 msgstr "acció «%s» incorrecta per a «%s»"
 
 #: builtin/am.c builtin/blame.c builtin/fetch.c builtin/pack-objects.c
-#: builtin/pull.c builtin/revert.c diff-merges.c environment.c gpg-interface.c
-#: ls-refs.c parallel-checkout.c sequencer.c setup.c
+#: builtin/pull.c builtin/revert.c diff-merges.c diff.c environment.c
+#: gpg-interface.c ls-refs.c parallel-checkout.c sequencer.c setup.c
 #, c-format
 msgid "invalid value for '%s': '%s'"
 msgstr "valor no vàlid per a «%s»: «%s»"
 
-#: builtin/am.c builtin/commit.c builtin/merge.c sequencer.c
+#: builtin/am.c builtin/commit.c builtin/merge.c sequencer.c trailer.c
 #, c-format
 msgid "could not read '%s'"
 msgstr "no s'ha pogut llegir «%s»"
@@ -2676,7 +2795,7 @@
 msgid "applying to an empty history"
 msgstr "s'està aplicant a una història buida"
 
-#: builtin/am.c builtin/commit.c builtin/merge.c builtin/replay.c sequencer.c
+#: builtin/am.c builtin/commit.c builtin/merge.c replay.c sequencer.c
 msgid "failed to write commit object"
 msgstr "s'ha produït un error en escriure l'objecte de comissió"
 
@@ -2865,7 +2984,8 @@
 #: builtin/am.c builtin/branch.c builtin/bugreport.c builtin/cat-file.c
 #: builtin/clone.c builtin/diagnose.c builtin/for-each-ref.c builtin/init-db.c
 #: builtin/ls-files.c builtin/ls-tree.c builtin/refs.c builtin/replace.c
-#: builtin/submodule--helper.c builtin/tag.c builtin/verify-tag.c
+#: builtin/repo.c builtin/submodule--helper.c builtin/tag.c
+#: builtin/verify-tag.c
 msgid "format"
 msgstr "format"
 
@@ -3005,23 +3125,36 @@
 
 #: builtin/backfill.c
 msgid "Minimum number of objects to request at a time"
-msgstr "Nombre mínim d'objectes a sol·licitar d'una vegada."
+msgstr "Nombre mínim d'objectes a sol·licitar d'una vegada"
 
 #: builtin/backfill.c
 msgid "Restrict the missing objects to the current sparse-checkout"
 msgstr "Restringeix els objectes que manquen al sparse-checkout actual"
 
-#: builtin/bisect.c
-msgid ""
-"git bisect start [--term-(new|bad)=<term> --term-(old|good)=<term>]    [--no-"
-"checkout] [--first-parent] [<bad> [<good>...]] [--]    [<pathspec>...]"
-msgstr ""
-"git bisect start [--term-(new|bad)=<term> --term-(old|good)=<term>]    [--no-"
-"checkout] [--first-parent] [<bad> [<good>...]] [--]    [<pathspec>...]"
+#: builtin/backfill.c builtin/diff-pairs.c builtin/log.c builtin/replay.c
+#: builtin/shortlog.c bundle.c
+#, c-format
+msgid "unrecognized argument: %s"
+msgstr "argument no reconegut: %s"
 
 #: builtin/bisect.c
-msgid "git bisect (good|bad) [<rev>...]"
-msgstr "git bisect (good|bad) [<revisió>...]"
+msgid ""
+"git bisect start [--term-(bad|new)=<term-new> --term-(good|old)=<term-old>]\n"
+"                 [--no-checkout] [--first-parent] [<bad> [<good>...]] [--] "
+"[<pathspec>...]"
+msgstr ""
+"git bisect start [--term-(bad|new)=<term-nou> --term-(good|old)=<term-"
+"antic>]\n"
+"                 [--no-checkout] [--first-parent] [<dolent> [<bo>...]] [--] "
+"[<especificació-camí>...]"
+
+#: builtin/bisect.c
+msgid "git bisect (bad|new|<term-new>) [<rev>]"
+msgstr "git bisect (bad|new|<term-nou>) [<revisió>]"
+
+#: builtin/bisect.c
+msgid "git bisect (good|old|<term-old>) [<rev>...]"
+msgstr "git bisect (good|old|<term-nou>) [<revisió>...]"
 
 #: builtin/bisect.c
 msgid "git bisect skip [(<rev>|<range>)...]"
@@ -3033,7 +3166,7 @@
 
 #: builtin/bisect.c
 msgid "git bisect replay <logfile>"
-msgstr "git bisect replay "
+msgstr "git bisect replay <fitxer-registre>"
 
 #: builtin/bisect.c
 msgid "git bisect run <cmd> [<arg>...]"
@@ -3372,6 +3505,18 @@
 msgid "must end with a color"
 msgstr "ha d'acabar amb un color"
 
+#: builtin/blame.c diff.c merge-ort.c transport.c
+#, c-format
+msgid "unknown value for config '%s': %s"
+msgstr "valor desconegut per al config «%s»': %s"
+
+#: builtin/blame.c builtin/merge-file.c diff.c
+msgid ""
+"option diff-algorithm accepts \"myers\", \"minimal\", \"patience\" and "
+"\"histogram\""
+msgstr ""
+"l'opció diff-algorithm accepta «myers», «minimal», «patience» i «histogram»"
+
 #: builtin/blame.c
 #, c-format
 msgid "cannot find revision %s to ignore"
@@ -3449,6 +3594,14 @@
 msgid "ignore whitespace differences"
 msgstr "ignora les diferències d'espai en blanc"
 
+#: builtin/blame.c builtin/merge-file.c diff.c
+msgid "<algorithm>"
+msgstr "<algorisme>"
+
+#: builtin/blame.c builtin/merge-file.c diff.c
+msgid "choose a diff algorithm"
+msgstr "trieu un algorisme per al diff"
+
 #: builtin/blame.c builtin/clone.c builtin/log.c
 msgid "rev"
 msgstr "rev"
@@ -3471,8 +3624,8 @@
 msgstr "acoloreix les línies per antiguitat"
 
 #: builtin/blame.c
-msgid "spend extra cycles to find better match"
-msgstr "gasta cicles extres per a trobar una coincidència millor"
+msgid "spend extra cycles to find a better match"
+msgstr "utilitza cicles addicionals per a trobar una coincidència millor"
 
 #: builtin/blame.c
 msgid "use revisions from <file> instead of calling git-rev-list"
@@ -3879,11 +4032,11 @@
 
 #: builtin/branch.c
 msgid ""
-"branch with --recurse-submodules can only be used if submodule."
-"propagateBranches is enabled"
+"branch with --recurse-submodules can only be used if "
+"submodule.propagateBranches is enabled"
 msgstr ""
-"la branca amb --recurse-submodules només es pot utilitzar si submodule."
-"propagateBranches està habilitat"
+"la branca amb --recurse-submodules només es pot utilitzar si "
+"submodule.propagateBranches està habilitat"
 
 #: builtin/branch.c
 msgid "--recurse-submodules can only be used to create branches"
@@ -4030,7 +4183,7 @@
 "Podeu eliminar qualsevol línia que vulgueu.\n"
 
 #: builtin/bugreport.c builtin/commit.c builtin/fast-export.c
-#: builtin/pack-objects.c builtin/rebase.c parse-options.h
+#: builtin/pack-objects.c builtin/rebase.c builtin/replay.c parse-options.h
 msgid "mode"
 msgstr "mode"
 
@@ -4442,7 +4595,7 @@
 msgid "read additional mailmap entries from file"
 msgstr "llegeix les entrades mailmap addicionals del fitxer"
 
-#: builtin/check-mailmap.c
+#: builtin/check-mailmap.c builtin/fast-import.c
 msgid "blob"
 msgstr "blob"
 
@@ -4513,22 +4666,6 @@
 msgstr "copia els fitxers des de «stage» amb nom"
 
 #: builtin/checkout.c
-msgid "git checkout [<options>] <branch>"
-msgstr "git checkout [<opcions>] <branca>"
-
-#: builtin/checkout.c
-msgid "git checkout [<options>] [<branch>] -- <file>..."
-msgstr "git checkout [<opcions>] [<branca>] -- <fitxer>..."
-
-#: builtin/checkout.c
-msgid "git switch [<options>] [<branch>]"
-msgstr "git switch [<opcions>] [<branca>]"
-
-#: builtin/checkout.c
-msgid "git restore [<options>] [--source=<branch>] <file>..."
-msgstr "git restore [<opcions>] [--source=<branca>] <fitxer>..."
-
-#: builtin/checkout.c
 #, c-format
 msgid "path '%s' does not have our version"
 msgstr "el camí «%s» no té la nostra versió"
@@ -4749,11 +4886,12 @@
 "Useu -- (i opcionalment --no-guess) per a desambiguar-ho"
 
 #: builtin/checkout.c
+#, c-format
 msgid ""
 "If you meant to check out a remote tracking branch on, e.g. 'origin',\n"
 "you can do so by fully qualifying the name with the --track option:\n"
 "\n"
-"    git checkout --track origin/<name>\n"
+"    git %s --track origin/<name>\n"
 "\n"
 "If you'd like to always have checkouts of an ambiguous <name> prefer\n"
 "one remote, e.g. the 'origin' remote, consider setting\n"
@@ -4762,7 +4900,7 @@
 "Si voleu agafar una branca de seguiment remota, p. ex. «origin», podeu\n"
 "fer-ho especificant el nom complet amb l'opció --track:\n"
 "\n"
-"    git checkout --track origin/<nom>\n"
+"    git %s --track origin/<nom>\n"
 "\n"
 "Si voleu que en agafar un branca amb un <nom> ambigu s'usi una branca\n"
 "remota, p. ex. «origin» al remot, considereu configurar l'opció\n"
@@ -4951,6 +5089,22 @@
 msgstr "no limitis les especificacions de camí només a entrades disperses"
 
 #: builtin/checkout.c
+msgid "git checkout [<options>] <branch>"
+msgstr "git checkout [<opcions>] <branca>"
+
+#: builtin/checkout.c
+msgid "git checkout [<options>] [<branch>] -- <file>..."
+msgstr "git checkout [<opcions>] [<branca>] -- <fitxer>..."
+
+#: builtin/checkout.c
+msgid "git switch [<options>] [<branch>]"
+msgstr "git switch [<opcions>] [<branca>]"
+
+#: builtin/checkout.c
+msgid "git restore [<options>] [--source=<branch>] <file>..."
+msgstr "git restore [<opcions>] [--source=<branca>] <fitxer>..."
+
+#: builtin/checkout.c
 #, c-format
 msgid "options '-%c', '-%c', and '%s' cannot be used together"
 msgstr "les opcions «-%c», «-%c», i «%s» no es poden usar juntes"
@@ -4964,10 +5118,10 @@
 msgid "missing branch name; try -%c"
 msgstr "hi manca el nom de la branca; proveu -%c"
 
-#: builtin/checkout.c
+#: builtin/checkout.c sequencer.c
 #, c-format
-msgid "could not resolve %s"
-msgstr "no es pot resoldre %s"
+msgid "could not resolve '%s'"
+msgstr "no s'ha pogut resoldre «%s»"
 
 #: builtin/checkout.c
 msgid "invalid path specification"
@@ -5064,14 +5218,14 @@
 "[<pathspec>...]"
 msgstr ""
 "git clean [-d] [-f] [-i] [-n] [-q] [-e <patró>] [-x | -X] [--] "
-"[<pathspec>...]"
+"[<especificació-camí>...]"
 
-#: builtin/clean.c
+#: builtin/clean.c builtin/sparse-checkout.c
 #, c-format
 msgid "Removing %s\n"
 msgstr "S'està eliminant %s\n"
 
-#: builtin/clean.c
+#: builtin/clean.c builtin/sparse-checkout.c
 #, c-format
 msgid "Would remove %s\n"
 msgstr "Eliminaria %s\n"
@@ -5195,7 +5349,7 @@
 msgid "do not print names of files removed"
 msgstr "no imprimeixis els noms dels fitxers eliminats"
 
-#: builtin/clean.c
+#: builtin/clean.c builtin/sparse-checkout.c
 msgid "force"
 msgstr "força"
 
@@ -5208,8 +5362,8 @@
 msgstr "elimina directoris sencers"
 
 #: builtin/clean.c builtin/config.c builtin/describe.c builtin/grep.c
-#: builtin/log.c builtin/ls-files.c builtin/name-rev.c builtin/pack-refs.c
-#: builtin/show-ref.c ref-filter.h
+#: builtin/log.c builtin/ls-files.c builtin/name-rev.c builtin/show-ref.c
+#: pack-refs.c ref-filter.h
 msgid "pattern"
 msgstr "patró"
 
@@ -5638,7 +5792,7 @@
 #: builtin/clone.c
 #, c-format
 msgid "Remote revision %s not found in upstream %s"
-msgstr "La branca remota %s no es troba en la font %s "
+msgstr "La branca remota %s no es troba en la font %s"
 
 #: builtin/clone.c
 msgid "You appear to have cloned an empty repository."
@@ -5773,8 +5927,7 @@
 
 #: builtin/commit-graph.c
 msgid "maximum number of commits in a non-base split commit-graph"
-msgstr ""
-"nombre màxim de comissions en un graf de comissions separades sense base"
+msgstr "nombre màxim de comissions en un graf de comissions dividit sense base"
 
 #: builtin/commit-graph.c
 msgid "maximum ratio between two levels of a split commit-graph"
@@ -5881,12 +6034,12 @@
 "           [--allow-empty-message] [--no-verify] [-e] [--author=<autor>]\n"
 "           [--date=<data>] [--cleanup=<mode>] [--[no-]status]\n"
 "           [-i | -o] [--pathspec-from-file=<fitxer> [--pathspec-file-nul]]\n"
-"           [(--trailer <token>[(=|:)<valor>])...] [-S[<id-clau>]]\n"
+"           [(--trailer <testimoni>[(=|:)<valor>])...] [-S[<id-clau>]]\n"
 "           [--] [<especificació-camí>...]"
 
 #: builtin/commit.c
 msgid "git status [<options>] [--] [<pathspec>...]"
-msgstr "git status [<opcions>] [--] [<pathspec>...]"
+msgstr "git status [<opcions>] [--] [<especificació-camí>...]"
 
 #: builtin/commit.c
 msgid ""
@@ -6050,7 +6203,8 @@
 msgid "could not read MERGE_MSG"
 msgstr "no s'ha pogut llegir MERGE_MSG"
 
-#: builtin/commit.c bundle.c rerere.c sequencer.c
+#: builtin/commit.c builtin/history.c builtin/submodule--helper.c bundle.c
+#: rerere.c sequencer.c
 #, c-format
 msgid "could not open '%s'"
 msgstr "no s'ha pogut obrir «%s»"
@@ -6367,11 +6521,11 @@
 msgid "the commit is authored by me now (used with -C/-c/--amend)"
 msgstr "l'autor de la comissió soc jo ara (s'usa amb -C/-c/--amend)"
 
-#: builtin/commit.c builtin/interpret-trailers.c builtin/tag.c
+#: builtin/commit.c builtin/interpret-trailers.c builtin/rebase.c builtin/tag.c
 msgid "trailer"
 msgstr "remolc"
 
-#: builtin/commit.c builtin/tag.c
+#: builtin/commit.c builtin/rebase.c builtin/tag.c
 msgid "add custom trailer(s)"
 msgstr "afegeix un «trailer» personalitzat"
 
@@ -6458,7 +6612,7 @@
 msgid "could not read commit message: %s"
 msgstr "no s'ha pogut llegir el missatge de comissió: %s"
 
-#: builtin/commit.c
+#: builtin/commit.c builtin/history.c
 #, c-format
 msgid "Aborting commit due to empty commit message.\n"
 msgstr "S'està avortant la comissió a causa d'un missatge de comissió buit.\n"
@@ -6854,10 +7008,22 @@
 #, c-format
 msgid ""
 "cannot overwrite multiple values with a single value\n"
-"       Use a regexp, --add or --replace-all to change %s."
+"       Use --value=<pattern>, --append or --all to change %s."
 msgstr ""
 "no es poden sobreescriure múltiples valors amb un sol valor\n"
-"       Useu una expressió regular, --add o --replace-all per a canviar %s."
+"       Useu --value=<patró>, --append, o --all per a canviar %s."
+
+# multi-valued → multivalor?
+#: builtin/config.c
+msgid "unset all multi-valued config options"
+msgstr "desactiva totes les opcions de configuració amb multiples valors"
+
+# multi-valued → multivalor?
+#: builtin/config.c
+msgid "unset multi-valued config options with matching values"
+msgstr ""
+"desassigna les opcions de configuració amb més d'un valor amb valors "
+"coincidents"
 
 #: builtin/config.c
 #, c-format
@@ -6976,6 +7142,15 @@
 msgid "--comment is only applicable to add/set/replace operations"
 msgstr "--comment només es pot aplicar a operacions add/set/replace"
 
+#: builtin/config.c
+#, c-format
+msgid ""
+"cannot overwrite multiple values with a single value\n"
+"       Use a regexp, --add or --replace-all to change %s."
+msgstr ""
+"no es poden sobreescriure múltiples valors amb un sol valor\n"
+"       Useu una expressió regular, --add o --replace-all per a canviar %s."
+
 #: builtin/count-objects.c
 msgid "print sizes in human readable format"
 msgstr "imprimeix les mides en un format llegible pels humans"
@@ -7100,6 +7275,16 @@
 
 #: builtin/describe.c
 #, c-format
+msgid "cannot search for blob '%s' on an unborn branch"
+msgstr "no es pot cercar el blob «%s» en una branca no nascuda"
+
+#: builtin/describe.c
+#, c-format
+msgid "blob '%s' not reachable from HEAD"
+msgstr "el tros «%s» no és accessible des de HEAD"
+
+#: builtin/describe.c
+#, c-format
 msgid "describe %s\n"
 msgstr "descriu %s\n"
 
@@ -7212,12 +7397,6 @@
 msgid "git diff-pairs -z [<diff-options>]"
 msgstr "git diff-pairs -z [<opcions-diferencia>]"
 
-#: builtin/diff-pairs.c builtin/log.c builtin/replay.c builtin/shortlog.c
-#: bundle.c
-#, c-format
-msgid "unrecognized argument: %s"
-msgstr "argument no reconegut: %s"
-
 #: builtin/diff-pairs.c
 msgid "working without -z is not supported"
 msgstr "no s'admet treballar sense -z"
@@ -7279,6 +7458,10 @@
 msgid "%s...%s: no merge base"
 msgstr "%s...%s: sense una base de fusió"
 
+#: builtin/diff.c setup.c
+msgid "cannot come back to cwd"
+msgstr "no es pot tornar al directori de treball actual"
+
 #: builtin/diff.c
 msgid "Not a git repository"
 msgstr "No és un repositori de git"
@@ -7420,10 +7603,171 @@
 msgstr "git fast-export [<rev-list-opts>]"
 
 #: builtin/fast-export.c
-msgid "Error: Cannot export nested tags unless --mark-tags is specified."
+#, c-format
+msgid "unknown %s mode: %s"
+msgstr "mode %s desconegut: %s"
+
+#: builtin/fast-export.c
+#, c-format
+msgid "unknown tag-of-filtered mode: %s"
+msgstr "mode tag-of-filtered desconegut: %s"
+
+#: builtin/fast-export.c
+#, c-format
+msgid "unknown reencoding mode: %s"
+msgstr "Mode de recodificació desconegut: %s"
+
+#: builtin/fast-export.c
+#, c-format
+msgid "could not read blob %s"
+msgstr "no s'ha pogut llegir el blob %s"
+
+#: builtin/fast-export.c
+#, c-format
+msgid "oid mismatch in blob %s"
+msgstr "l'oid no coincideix en el blob %s"
+
+#: builtin/fast-export.c
+#, c-format
+msgid "could not write blob '%s'"
+msgstr "no s'ha pogut escriure el blob «%s»"
+
+#: builtin/fast-export.c
+#, c-format
+msgid "unexpected comparison status '%c' for %s, %s"
+msgstr "estat de comparació inesperat «%c» per a %s, %s"
+
+#: builtin/fast-export.c
+msgid "none"
+msgstr "cap"
+
+#: builtin/fast-export.c
+#, c-format
+msgid "could not find author in commit %s"
+msgstr "no s'ha pogut trobar l'autor en la comissió %s"
+
+#: builtin/fast-export.c
+#, c-format
+msgid "could not find committer in commit %s"
+msgstr "no s'ha pogut trobar el comitent en la comissió %s"
+
+#: builtin/fast-export.c
+#, c-format
+msgid ""
+"encountered commit-specific encoding %.*s in commit %s; use --reencode=[yes|"
+"no] to handle it"
 msgstr ""
-"Error: no es poden exportar les etiquetes imbricades a menys que "
-"s'especifiqui --mark-tags."
+"s'ha trobat una codificació específica d'una comissió %.*s en la comissió "
+"%s; useu --reencode=[yes|no] per a gestionar-la"
+
+#: builtin/fast-export.c
+#, c-format
+msgid "exporting %<PRIuMAX> signature(s) for commit %s"
+msgstr "exportant %<PRIuMAX> signatura/es per a la comissió %s"
+
+#: builtin/fast-export.c
+#, c-format
+msgid "stripping signature(s) from commit %s"
+msgstr "eliminant signatura/es de la comissió %s"
+
+#: builtin/fast-export.c
+#, c-format
+msgid "encountered signed commit %s; use --signed-commits=<mode> to handle it"
+msgstr ""
+"s'ha trobat una comissió signada %s; useu --signed-commits=<mode> per a "
+"gestionar-la"
+
+#: builtin/fast-export.c
+#, c-format
+msgid ""
+"omitting tag %s,\n"
+"since tags of trees (or tags of tags of trees, etc.) are not supported."
+msgstr ""
+"s'omet l'etiqueta %s,\n"
+"ja que les etiquetes d'arbre (o les etiquetes d'etiquetes d'arbre) no són "
+"admeses."
+
+#: builtin/fast-export.c
+#, c-format
+msgid "could not read tag %s"
+msgstr "no s'ha pogut llegir la etiqueta %s"
+
+#: builtin/fast-export.c
+#, c-format
+msgid "exporting signed tag %s"
+msgstr "exportant l'etiqueta signada %s"
+
+#: builtin/fast-export.c
+#, c-format
+msgid "stripping signature from tag %s"
+msgstr "eliminant la signatura de l'etiqueta %s"
+
+#: builtin/fast-export.c
+#, c-format
+msgid "encountered signed tag %s; use --signed-tags=<mode> to handle it"
+msgstr ""
+"s'ha trobat l'etiqueta signada %s; useu --signed-tags=<mode> per a gestionar-"
+"la"
+
+#: builtin/fast-export.c
+#, c-format
+msgid ""
+"tag %s tags unexported object; use --tag-of-filtered-object=<mode> to handle "
+"it"
+msgstr ""
+"l'etiqueta %s etiqueta un objecte no exportat; useu --tag-of-filtered-"
+"object=<mode> per a gestionar-lo"
+
+#: builtin/fast-export.c
+msgid "cannot export nested tags unless --mark-tags is specified."
+msgstr ""
+"no es poden exportar les etiquetes imbricades a menys que s'especifiqui --"
+"mark-tags."
+
+#: builtin/fast-export.c
+#, c-format
+msgid "tag %s points nowhere?"
+msgstr "l'etiqueta %s no apunta a enlloc?"
+
+#: builtin/fast-export.c
+#, c-format
+msgid "%s: unexpected object of type %s, skipping."
+msgstr "%s: objecte de tipus %s no esperat; s'omet."
+
+#: builtin/fast-export.c
+#, c-format
+msgid "tag points to object of unexpected type %s, skipping."
+msgstr "l'etiqueta apunta a un objecte de tipus inesperat %s, ometent."
+
+#: builtin/fast-export.c
+#, c-format
+msgid "unable to open marks file %s for writing."
+msgstr "no s'ha pogut obrir el fitxer de marques %s per a escriptura."
+
+#: builtin/fast-export.c
+#, c-format
+msgid "unable to write marks file %s."
+msgstr "no s'ha pogut escriure el fitxer de marques %s."
+
+#: builtin/fast-export.c builtin/fast-import.c
+#, c-format
+msgid "corrupt mark line: %s"
+msgstr "línia de marca malmesa: %s"
+
+#: builtin/fast-export.c builtin/fast-import.c fetch-pack.c
+#, c-format
+msgid "object not found: %s"
+msgstr "objecte no trobat: %s"
+
+#: builtin/fast-export.c
+#, c-format
+msgid "not a commit? can't happen: %s"
+msgstr "no és una comissió? No pot passar: %s"
+
+#: builtin/fast-export.c
+#, c-format
+msgid "object %s already has a mark"
+msgstr "l'objecte %s ja té una marca"
 
 #: builtin/fast-export.c
 msgid "--anonymize-map token cannot be empty"
@@ -7497,7 +7841,7 @@
 
 #: builtin/fast-export.c
 msgid "convert <from> to <to> in anonymized output"
-msgstr "converteix <from> a <to> en una sortida anònima"
+msgstr "converteix <des-de> a <cap-a> en una sortida anònima"
 
 #: builtin/fast-export.c
 msgid "reference parents which are not in fast-export stream by object id"
@@ -7515,36 +7859,652 @@
 
 #: builtin/fast-import.c
 #, c-format
-msgid "Missing from marks for submodule '%s'"
-msgstr "Falten les marques «from» per al submòdul «%s»"
+msgid "can't write crash report %s"
+msgstr "no es pot escriure l'informe de fallada %s"
 
 #: builtin/fast-import.c
 #, c-format
-msgid "Missing to marks for submodule '%s'"
-msgstr "Falten les marques per al submòdul «%s»"
+msgid "fast-import: dumping crash report to %s\n"
+msgstr "fast-import: es bolca l'informe de fallada a %s\n"
 
 #: builtin/fast-import.c
 #, c-format
-msgid "Expected 'mark' command, got %s"
-msgstr "S'esperava l'ordre «mark», s'ha rebut %s"
+msgid "mark :%<PRIuMAX> not declared"
+msgstr "marca: %<PRIuMAX> no declarada"
 
 #: builtin/fast-import.c
 #, c-format
-msgid "Expected 'to' command, got %s"
-msgstr "S'esperava l'ordre «to», s'ha rebut «%s»"
+msgid "invalid attempt to create duplicate branch: %s"
+msgstr "intent invàlid de crear una branca duplicada: %s"
 
 #: builtin/fast-import.c
-msgid "Expected format name:filename for submodule rewrite option"
+#, c-format
+msgid "branch name doesn't conform to Git standards: %s"
+msgstr "el nom de la branca no és compatible amb els estàndards de Git: %s"
+
+#: builtin/fast-import.c
+msgid "internal consistency error creating the index"
+msgstr "error de consistència intern en crear l'índex"
+
+#: builtin/fast-import.c
+msgid "cannot create keep file"
+msgstr "no s'ha pogut crear el fitxer de preservació"
+
+#: builtin/fast-import.c
+msgid "failed to write keep file"
+msgstr "no s'ha pogut escriure el fitxer de preservació"
+
+#: builtin/fast-import.c
+msgid "cannot store pack file"
+msgstr "no s'ha pogut emmagatzemar el fitxer de paquet"
+
+#: builtin/fast-import.c
+msgid "cannot store index file"
+msgstr "no es pot emmagatzemar el fitxer d'índex"
+
+#: builtin/fast-import.c
+#, c-format
+msgid "failed seeking to start of '%s'"
+msgstr "s'ha produït un error fent seek a l'inici de «%s»"
+
+#: builtin/fast-import.c
+#, c-format
+msgid "core Git rejected index %s"
+msgstr "el nucli de Git ha rebutjat l'índex %s"
+
+#: builtin/fast-import.c
+msgid "cannot truncate pack to skip duplicate"
+msgstr "no s'ha pogut truncar el paquet per a saltar el duplicat"
+
+#: builtin/fast-import.c
+#, c-format
+msgid "EOF in data (%<PRIuMAX> bytes remaining)"
+msgstr "EOF en les dades (quan encara queden %<PRIuMAX> octets):"
+
+#: builtin/fast-import.c
+#, c-format
+msgid "unexpected deflate failure: %d"
+msgstr "fallada de compressió inesperada: %d"
+
+#: builtin/fast-import.c
+#, c-format
+msgid "not a tree: %s"
+msgstr "no és un arbre: %s"
+
+#: builtin/fast-import.c
+#, c-format
+msgid "can't load tree %s"
+msgstr "no es pot carregar l'arbre %s"
+
+#: builtin/fast-import.c
+#, c-format
+msgid "corrupt mode in %s"
+msgstr "mode corrupte en «%s»"
+
+#: builtin/fast-import.c
+msgid "root cannot be a non-directory"
+msgstr "l'arrel no pot ser un no-directori"
+
+#: builtin/fast-import.c
+msgid "empty path component found in input"
+msgstr "s'ha trobat un component de camí buit en l'entrada"
+
+#: builtin/fast-import.c
+msgid "non-directories cannot have subtrees"
+msgstr "els no-directoris no pòden tenir subarbres"
+
+#: builtin/fast-import.c
+#, c-format
+msgid "dropping %s since it would point to itself (i.e. to %s)"
+msgstr "descartant %s, ja que s'apuntaria a si mateix (és a dir, a %s)"
+
+#: builtin/fast-import.c
+#, c-format
+msgid "branch %s is missing commits."
+msgstr "manquen comissions en la branca %s."
+
+#: builtin/fast-import.c
+#, c-format
+msgid "not updating %s (new tip %s does not contain %s)"
+msgstr "no s'actualitza %s (la nova punta %s no conté %s)"
+
+#: builtin/fast-import.c builtin/sparse-checkout.c commit-graph.c midx-write.c
+#: sequencer.c
+#, c-format
+msgid "unable to create leading directories of %s"
+msgstr "no s'han pogut crear els directoris inicials de «%s»"
+
+#: builtin/fast-import.c
+#, c-format
+msgid "unable to write marks file %s"
+msgstr "no s'ha pogut escriure el fitxer de marques %s"
+
+#: builtin/fast-import.c
+#, c-format
+msgid "unable to write marks file %s: %s"
+msgstr "no s'ha pogut escriure el fitxer de marques %s: %s"
+
+#: builtin/fast-import.c object-file.c
+#, c-format
+msgid "unable to write file %s"
+msgstr "no s'ha pogut escriure al fitxer %s"
+
+#: builtin/fast-import.c
+#, c-format
+msgid "expected 'data n' command, found: %s"
+msgstr "s'esperava l'ordre «data n», s'ha rebut: %s"
+
+#: builtin/fast-import.c
+#, c-format
+msgid "EOF in data (terminator '%s' not found)"
+msgstr "EOF en les dades (no s'ha trobat el terminador «%s»)"
+
+#: builtin/fast-import.c
+msgid "data is too large to use in this context"
+msgstr "les dades són massa grans per a usar-les en aquest context"
+
+#: builtin/fast-import.c
+#, c-format
+msgid "EOF in data (%lu bytes remaining)"
+msgstr "EOF en les dades (quan quedaven %lu octets)"
+
+#: builtin/fast-import.c
+#, c-format
+msgid "missing < in ident string: %s"
+msgstr "manca < en la cadena de l'identificador: %s"
+
+#: builtin/fast-import.c
+#, c-format
+msgid "missing space before < in ident string: %s"
+msgstr "manca un espai davant de < en la cadena d'identificador %s"
+
+#: builtin/fast-import.c
+#, c-format
+msgid "missing > in ident string: %s"
+msgstr "manca > en la cadena de l'identificador: %s"
+
+#: builtin/fast-import.c
+#, c-format
+msgid "missing space after > in ident string: %s"
+msgstr "manca un espai després de > en la cadena d'identificador %s"
+
+#: builtin/fast-import.c
+#, c-format
+msgid "invalid raw date \"%s\" in ident: %s"
+msgstr "data en brut no vàlida \"%s\" en l'identificador %s"
+
+#: builtin/fast-import.c
+#, c-format
+msgid "invalid rfc2822 date \"%s\" in ident: %s"
+msgstr "data rfc2822 no vàlida \"%s\" en l'identificador %s"
+
+#: builtin/fast-import.c
+#, c-format
+msgid "date in ident must be 'now': %s"
+msgstr "la data del identificador ha de ser 'now': %s"
+
+#: builtin/fast-import.c
+#, c-format
+msgid "too large fanout (%u)"
+msgstr "la taula de distribució és massa gran (%u)"
+
+#: builtin/fast-import.c
+#, c-format
+msgid "failed to remove path %s"
+msgstr "no s'ha pogut eliminar el camí %s"
+
+#: builtin/fast-import.c
+#, c-format
+msgid "no value after ':' in mark: %s"
+msgstr "no hi ha cap valor després de «:» en la marca: %s"
+
+#: builtin/fast-import.c
+#, c-format
+msgid "garbage after mark: %s"
+msgstr "escombraries després de la marca: %s"
+
+#: builtin/fast-import.c
+#, c-format
+msgid "missing space after mark: %s"
+msgstr "falta un espai després de la marca: %s"
+
+#: builtin/fast-import.c
+#, c-format
+msgid "invalid %s: %s"
+msgstr "%s no vàlid: %s"
+
+#: builtin/fast-import.c
+#, c-format
+msgid "NUL in %s: %s"
+msgstr "NUL en %s: %s"
+
+#: builtin/fast-import.c
+#, c-format
+msgid "garbage after %s: %s"
+msgstr "brossa després de  %s: %s"
+
+#: builtin/fast-import.c
+#, c-format
+msgid "missing space after %s: %s"
+msgstr "manca un espai després de %s: %s"
+
+#: builtin/fast-import.c
+#, c-format
+msgid "corrupt mode: %s"
+msgstr "mode malmès: %s"
+
+#: builtin/fast-import.c
+#, c-format
+msgid "invalid dataref: %s"
+msgstr "referència de dades no vàlida: «%s»"
+
+#: builtin/fast-import.c
+#, c-format
+msgid "missing space after SHA1: %s"
+msgstr "manca l'espai després de SHA1: %s"
+
+#: builtin/fast-import.c
+#, c-format
+msgid "Git links cannot be specified 'inline': %s"
+msgstr "Els enllaços Git no es poden especificar «en línia»: %s"
+
+#: builtin/fast-import.c
+#, c-format
+msgid "not a commit (actually a %s): %s"
+msgstr "no és una comissió (de fet, és un %s): %s"
+
+#: builtin/fast-import.c
+#, c-format
+msgid "directories cannot be specified 'inline': %s"
+msgstr "els directoris no es poden especificar «en línia»: %s"
+
+#: builtin/fast-import.c
+#, c-format
+msgid "%s not found: %s"
+msgstr "%s no trobat: %s"
+
+#: builtin/fast-import.c
+msgid "tree"
+msgstr "arbre"
+
+#: builtin/fast-import.c
+#, c-format
+msgid "not a %s (actually a %s): %s"
+msgstr "no és un %s (de fet, és un %s): %s"
+
+#: builtin/fast-import.c
+#, c-format
+msgid "path %s not in branch"
+msgstr "el camí %s no és a la branca"
+
+#: builtin/fast-import.c
+msgid "can't add a note on empty branch."
+msgstr "no es pot afegir una nota a una branca buida."
+
+#: builtin/fast-import.c
+#, c-format
+msgid "mark :%<PRIuMAX> not a commit"
+msgstr "marca: %<PRIuMAX> no és una comissió"
+
+#: builtin/fast-import.c
+#, c-format
+msgid "not a valid commit: %s"
+msgstr "no és una comissió vàlida: %s"
+
+#: builtin/fast-import.c
+#, c-format
+msgid "invalid ref name or SHA1 expression: %s"
+msgstr "nom de referència o expressió SHA1 invàlids: %s"
+
+#: builtin/fast-import.c
+#, c-format
+msgid "not a blob (actually a %s): %s"
+msgstr "no és un blob (de fet, és un %s): %s"
+
+#: builtin/fast-import.c
+#, c-format
+msgid "blob not found: %s"
+msgstr "blob no trobat: %s"
+
+#: builtin/fast-import.c
+#, c-format
+msgid "the commit %s is corrupt"
+msgstr "la comissió %s està malmesa"
+
+#: builtin/fast-import.c
+#, c-format
+msgid "can't create a branch from itself: %s"
+msgstr "no s'ha pogut crear una branca a partir d'ella mateixa: %s"
+
+#: builtin/fast-import.c
+#, c-format
+msgid ""
+"expected gpgsig format: 'gpgsig <hash-algo> <signature-format>', got 'gpgsig "
+"%s'"
 msgstr ""
-"S'esperava el format «nom:nom de fitxer» per a l'opció de reescriptura de "
+"s'esperava aquest format de gpgsig: «gpgsig <hash-algo> <format-signatura>», "
+"però s'ha rebut «gpgsig %s»"
+
+#: builtin/fast-import.c
+#, c-format
+msgid "unknown git hash algorithm in gpgsig: '%s'"
+msgstr "algorisme de hash desconegut en gpgsig: «%s»"
+
+#: builtin/fast-import.c
+#, c-format
+msgid "invalid signature format in gpgsig: '%s'"
+msgstr "format de signatura invàlid en gpgsig: «%s»"
+
+#: builtin/fast-import.c
+msgid "'unknown' signature format in gpgsig"
+msgstr "format de signatura «unknown» en gpgsig"
+
+#: builtin/fast-import.c
+#, c-format
+msgid "multiple %s signatures found, ignoring additional signature"
+msgstr "s'han trobat diverses signatures %s; s'ignora la signatura addicional"
+
+#: builtin/fast-import.c
+msgid "parse_one_signature() returned unknown hash algo"
+msgstr "parse_one_signature() ha retornat un algorisme de hash desconegut"
+
+#: builtin/fast-import.c builtin/fsck.c
+msgid "unknown"
+msgstr "desconegut"
+
+#: builtin/fast-import.c
+#, c-format
+msgid ""
+"stripping invalid signature for commit '%.100s...'\n"
+"  allegedly by %s"
+msgstr ""
+"eliminant la signatura invàlida de la comissió «%.100s...»\n"
+"  presumptament de %s"
+
+#: builtin/fast-import.c
+#, c-format
+msgid ""
+"stripping invalid signature for commit '%.*s'\n"
+"  allegedly by %s"
+msgstr ""
+"eliminant la signatura invàlida de la comissió «%.*s»\n"
+"  presumptament de %s"
+
+#: builtin/fast-import.c
+#, c-format
+msgid ""
+"stripping invalid signature for commit\n"
+"  allegedly by %s"
+msgstr ""
+"eliminant la signatura invàlida de la comissió\n"
+"  presumptament de %s"
+
+#: builtin/fast-import.c
+#, c-format
+msgid ""
+"replacing invalid signature for commit '%.100s...'\n"
+"  allegedly by %s"
+msgstr ""
+"eliminant la signatura invàlida de la comissió «%.100s...»\n"
+"  presumptament de %s"
+
+#: builtin/fast-import.c
+#, c-format
+msgid ""
+"replacing invalid signature for commit '%.*s'\n"
+"  allegedly by %s"
+msgstr ""
+"eliminant la signatura invàlida de la comissió «%.*s»\n"
+"  presumptament de %s"
+
+#: builtin/fast-import.c
+#, c-format
+msgid ""
+"replacing invalid signature for commit\n"
+"  allegedly by %s"
+msgstr ""
+"eliminant la signatura invàlida de la comissió\n"
+"  presumptament de %s"
+
+#: builtin/fast-import.c
+msgid "aborting due to invalid signature"
+msgstr "s'està avortant la comissió a causa d'una signatura invàlida"
+
+#: builtin/fast-import.c
+msgid "signing commits in interoperability mode is unsupported"
+msgstr "no s'admet la signatura de comissions en mode d'interoperabilitat"
+
+#: builtin/fast-import.c
+msgid "failed to sign commit object"
+msgstr "no s'ha pogut signar l'objecte de comissió"
+
+#: builtin/fast-import.c
+msgid "expected committer but didn't get one"
+msgstr "s'esperava un comitent però no se n'ha rebut cap"
+
+#: builtin/fast-import.c
+msgid "encountered signed commit; use --signed-commits=<mode> to handle it"
+msgstr ""
+"s'ha trobat una comissió signada; useu --signed-commits=<mode> per a "
+"gestionar-la"
+
+#: builtin/fast-import.c
+msgid "stripping a commit signature"
+msgstr "eliminant una signatura de comissió"
+
+#: builtin/fast-import.c
+msgid "importing a commit signature verbatim"
+msgstr "important una firma de comissió de manera literal"
+
+#: builtin/fast-import.c
+msgid "failed to sign tag object"
+msgstr "no s'ha pogut signar l'objecte d'etiqueta"
+
+#: builtin/fast-import.c
+#, c-format
+msgid "importing a tag signature verbatim for tag '%s'"
+msgstr "important una signatura d'etiqueta literalment per a l'etiqueta «%s»"
+
+#: builtin/fast-import.c
+#, c-format
+msgid "stripping a tag signature for tag '%s'"
+msgstr "s'està eliminant la signatura d'etiqueta de l'etiqueta «%s»"
+
+#: builtin/fast-import.c
+msgid "encountered signed tag; use --signed-tags=<mode> to handle it"
+msgstr ""
+"s'ha trobat una etiqueta signada; useu --signed-tags=<mode> per a gestionar-"
+"la"
+
+#: builtin/fast-import.c
+#, c-format
+msgid "expected 'from' command, got '%s'"
+msgstr "s'esperava l'ordre «from», s'ha rebut «%s»"
+
+#: builtin/fast-import.c
+msgid "can't tag an empty branch."
+msgstr "no es pot etiquetar una branca buida."
+
+#: builtin/fast-import.c builtin/merge.c
+#, c-format
+msgid "not a valid object: %s"
+msgstr "no és un objecte vàlid: %s"
+
+#: builtin/fast-import.c
+msgid "write to frontend failed"
+msgstr "l'escriptura a la interfície ha fallat"
+
+#: builtin/fast-import.c
+#, c-format
+msgid "can't read object %s"
+msgstr "no es pot llegir l'objecte %s"
+
+#: builtin/fast-import.c
+#, c-format
+msgid "object %s is a %s but a blob was expected."
+msgstr "l'objecte %s és un %s, però s'esperava un blob."
+
+#: builtin/fast-import.c
+#, c-format
+msgid "not a mark: %s"
+msgstr "no és una marca: %s"
+
+#: builtin/fast-import.c
+#, c-format
+msgid "unknown mark: %s"
+msgstr "marca desconeguda: %s"
+
+#: builtin/fast-import.c
+#, c-format
+msgid "garbage after SHA1: %s"
+msgstr "escombraries després del SHA1: %s"
+
+#: builtin/fast-import.c
+#, c-format
+msgid "not a tree-ish: %s"
+msgstr "«%s» no és un arbre o similar"
+
+#: builtin/fast-import.c
+#, c-format
+msgid "can't load object %s"
+msgstr "no es pot carregar l'objecte %s"
+
+#: builtin/fast-import.c
+#, c-format
+msgid "invalid SHA1 in tag: %s"
+msgstr "SHA1 no vàlid en l'etiqueta: %s"
+
+#: builtin/fast-import.c
+#, c-format
+msgid "invalid SHA1 in commit: %s"
+msgstr "SHA1 no vàlid en la comissió: %s"
+
+#: builtin/fast-import.c
+#, c-format
+msgid "missing from marks for submodule '%s'"
+msgstr "marques «from» absents per al submòdul «%s»"
+
+#: builtin/fast-import.c
+#, c-format
+msgid "missing to marks for submodule '%s'"
+msgstr "marques «to» absents per al submòdul «%s»"
+
+#: builtin/fast-import.c
+#, c-format
+msgid "missing space after tree-ish: %s"
+msgstr "falta un espai després de l'arbre o similar: %s"
+
+#: builtin/fast-import.c
+#, c-format
+msgid "not in a commit: %s"
+msgstr "no és en una comissió: %s"
+
+#: builtin/fast-import.c
+#, c-format
+msgid "expected 'mark' command, got %s"
+msgstr "s'esperava l'ordre «mark», s'ha rebut %s"
+
+#: builtin/fast-import.c
+#, c-format
+msgid "expected 'to' command, got %s"
+msgstr "s'esperava l'ordre «to», s'ha rebut «%s»"
+
+#: builtin/fast-import.c
+msgid "only one import-marks command allowed per stream"
+msgstr "només es permet una ordre import-marks per flux"
+
+#: builtin/fast-import.c
+#, c-format
+msgid "unknown --date-format argument %s"
+msgstr "argument de --date-format desconegut %s"
+
+#: builtin/fast-import.c
+#, c-format
+msgid "%s: argument must be a non-negative integer"
+msgstr "%s l'argument ha de ser un enter no negatiu"
+
+#: builtin/fast-import.c
+#, c-format
+msgid "--depth cannot exceed %u"
+msgstr "--depth no pot superar %u"
+
+#: builtin/fast-import.c
+#, c-format
+msgid "--cat-blob-fd cannot exceed %d"
+msgstr "--cat-blob-fd no pot superar %d"
+
+#: builtin/fast-import.c
+msgid "expected format name:filename for submodule rewrite option"
+msgstr ""
+"s'esperava el format «nom:nom-de-fitxer» per a l'opció de reescriptura de "
 "submòdul"
 
 #: builtin/fast-import.c
 #, c-format
+msgid "max-pack-size is now in bytes, assuming --max-pack-size=%lum"
+msgstr ""
+"max-pack-size s'expressa ara en octets; s'assumeix --max-pack-size=%lum"
+
+#: builtin/fast-import.c
+msgid "minimum max-pack-size is 1 MiB"
+msgstr "la mida mínima de max-pack-size és 1 MiB"
+
+#: builtin/fast-import.c
+#, c-format
+msgid "unknown --signed-commits mode '%s'"
+msgstr "mode de --signed-commits desconegut «%s»"
+
+#: builtin/fast-import.c
+#, c-format
+msgid "unknown --signed-tags mode '%s'"
+msgstr "mode desconegut de --signed-tags «%s»"
+
+#: builtin/fast-import.c
+#, c-format
 msgid "feature '%s' forbidden in input without --allow-unsafe-features"
 msgstr ""
 "característica «%s» prohibida a l'entrada sense --allow-unsafe-features"
 
+#: builtin/fast-import.c
+#, c-format
+msgid "got feature command '%s' after data command"
+msgstr ""
+"s'ha rebut una ordre de característica «%s» després de l'ordre de dades"
+
+#: builtin/fast-import.c
+#, c-format
+msgid "this version of fast-import does not support feature %s."
+msgstr "aquesta versió de fast-import no admet la característica %s."
+
+#: builtin/fast-import.c
+#, c-format
+msgid "got option command '%s' after data command"
+msgstr "s'ha rebut una ordre d'opció «%s» després de l'ordre de dades"
+
+#: builtin/fast-import.c
+#, c-format
+msgid "this version of fast-import does not support option: %s"
+msgstr "aquesta versió de fast-import no admet l'opció: %s"
+
+#: builtin/fast-import.c builtin/merge-recursive.c
+#, c-format
+msgid "unknown option %s"
+msgstr "opció desconeguda %s"
+
+#: builtin/fast-import.c
+#, c-format
+msgid "unknown option --%s"
+msgstr "opció desconeguda --%s"
+
+#: builtin/fast-import.c
+#, c-format
+msgid "unsupported command: %s"
+msgstr "ordre no compatible: %s"
+
+#: builtin/fast-import.c
+msgid "stream ends early"
+msgstr "el flux acaba prematurament"
+
 #: builtin/fetch-pack.c
 #, c-format
 msgid "Lockfile created but not reported: %s"
@@ -7559,7 +8519,7 @@
 msgstr "git fetch [<opcions>] <grup>"
 
 #: builtin/fetch.c
-msgid "git fetch --multiple [<options>] [(<repository> | <group>)...]"
+msgid "git fetch --multiple [<options>] [(<repository>|<group>)...]"
 msgstr "git fetch --multiple [<opcions>] [(<repositori> | <grup>)...]"
 
 #: builtin/fetch.c
@@ -7717,6 +8677,38 @@
 "es deshabilitarà l'avís fins que el remot canviï HEAD a alguna altra cosa."
 
 #: builtin/fetch.c
+msgid ""
+"You're on a case-insensitive filesystem, and the remote you are\n"
+"trying to fetch from has references that only differ in casing. It\n"
+"is impossible to store such references with the 'files' backend. You\n"
+"can either accept this as-is, in which case you won't be able to\n"
+"store all remote references on disk. Or you can alternatively\n"
+"migrate your repository to use the 'reftable' backend with the\n"
+"following command:\n"
+"\n"
+"    git refs migrate --ref-format=reftable\n"
+"\n"
+"Please keep in mind that not all implementations of Git support this\n"
+"new format yet. So if you use tools other than Git to access this\n"
+"repository it may not be an option to migrate to reftables.\n"
+msgstr ""
+"Sou en un sistema que no distingeix majúscules i minúscules\n"
+"i el remot del qual esteu intentant obtenir té referències que\n"
+"només es diferencien en l'ús de majúscules i minúscules.\n"
+"És impossible guardar aquestes referències amb el motor \n"
+"d'emmagatzematge 'files'. Podeu acceptar-ho tal com és;\n"
+"en aquest cas, podreu guardar totes les referències remotes\n"
+"en disc. Alternativament, podeu migrar el vostre repositori\n"
+"perquè use el motor d'emmagatzematge 'reftable' amb l'ordre:\n"
+"\n"
+"    git refs migrate --ref-format=reftable\n"
+"\n"
+"Tingueu en compte que no totes les implementacions de Git\n"
+"admeten aquest format encara. Per tant, si useu eines diferents\n"
+"de Git per a accedir a aquest repositori, potser no és una opció\n"
+"migrar a 'reftables'.\n"
+
+#: builtin/fetch.c
 #, c-format
 msgid ""
 "some local refs could not be updated; try running\n"
@@ -7983,11 +8975,11 @@
 
 #: builtin/fetch.c
 msgid ""
-"--filter can only be used with the remote configured in extensions."
-"partialclone"
+"--filter can only be used with the remote configured in "
+"extensions.partialclone"
 msgstr ""
-"--filter només es pot utilitzar amb el remot configurat en extensions."
-"partialclone"
+"--filter només es pot utilitzar amb el remot configurat en "
+"extensions.partialclone"
 
 #: builtin/fetch.c
 msgid "--atomic can only be used when fetching from one remote"
@@ -8028,27 +9020,6 @@
 msgstr "fitxer del qual llegir"
 
 #: builtin/for-each-ref.c
-msgid "git for-each-ref [<options>] [<pattern>]"
-msgstr "git for-each-ref [<opcions>] [<patró>]"
-
-#: builtin/for-each-ref.c
-msgid "git for-each-ref [--points-at <object>]"
-msgstr "git for-each-ref [--points-at <objecte>]"
-
-#: builtin/for-each-ref.c
-msgid "git for-each-ref [--merged [<commit>]] [--no-merged [<commit>]]"
-msgstr "git for-each-ref [--merged [<comissió>]] [--no-merged [<comissió>]]"
-
-#: builtin/for-each-ref.c
-msgid "git for-each-ref [--contains [<commit>]] [--no-contains [<commit>]]"
-msgstr ""
-"git for-each-ref [--contains [<comissió>]] [--no-contains [<comissió>]]"
-
-#: builtin/for-each-ref.c
-msgid "git for-each-ref [--start-after <marker>]"
-msgstr "git for-each-ref [--start-after <marcador>]"
-
-#: builtin/for-each-ref.c
 msgid "quote placeholders suitably for shells"
 msgstr ""
 "posa els marcadors de posició entre cometes adequades per a intèrprets "
@@ -8123,6 +9094,10 @@
 msgid "cannot use --start-after with patterns"
 msgstr "no es pot utilitzar --start-after amb patrons"
 
+#: builtin/for-each-ref.c
+msgid "git for-each-ref "
+msgstr "git for-each-ref "
+
 #: builtin/for-each-repo.c
 msgid "git for-each-repo --config=<config> [--] <arguments>"
 msgstr "git for-each-repo --config=<config> [--] <arguments>"
@@ -8148,10 +9123,6 @@
 msgid "got bad config --config=%s"
 msgstr "s'ha obtingut una configuració incorrecta --config=%s"
 
-#: builtin/fsck.c
-msgid "unknown"
-msgstr "desconegut"
-
 #. TRANSLATORS: e.g. error in tree 01bfda: <more explanation>
 #: builtin/fsck.c
 #, c-format
@@ -8271,6 +9242,11 @@
 msgstr "%s: no és una comissió"
 
 #: builtin/fsck.c
+#, c-format
+msgid "invalid parameter: expected sha1, got '%s'"
+msgstr "paràmetre no vàlid: s'esperava sha1, s'ha obtingut «%s»"
+
+#: builtin/fsck.c
 msgid "notice: No default references"
 msgstr "avís: no hi ha referències per defecte"
 
@@ -8304,31 +9280,6 @@
 
 #: builtin/fsck.c
 #, c-format
-msgid "Checking %s link"
-msgstr "S'està comprovant l'enllaç %s"
-
-#: builtin/fsck.c builtin/index-pack.c
-#, c-format
-msgid "invalid %s"
-msgstr "%s no vàlid"
-
-#: builtin/fsck.c
-#, c-format
-msgid "%s points to something strange (%s)"
-msgstr "%s apunta a una cosa estranya (%s)"
-
-#: builtin/fsck.c
-#, c-format
-msgid "%s: detached HEAD points at nothing"
-msgstr "%s: la HEAD separada no apunta a res"
-
-#: builtin/fsck.c
-#, c-format
-msgid "notice: %s points to an unborn branch (%s)"
-msgstr "avís: %s apunta a una branca no nascuda (%s)"
-
-#: builtin/fsck.c
-#, c-format
 msgid "Checking cache tree of %s"
 msgstr "S'està comprovant l'arbre de la memòria cau %s"
 
@@ -8412,7 +9363,7 @@
 msgid "write dangling objects in .git/lost-found"
 msgstr "escriu objectes despenjats a .git/lost-found"
 
-#: builtin/fsck.c builtin/prune.c
+#: builtin/fsck.c builtin/prune.c builtin/repo.c
 msgid "show progress"
 msgstr "mostra el progrés"
 
@@ -8428,16 +9379,6 @@
 msgid "Checking objects"
 msgstr "S'estan comprovant els objectes"
 
-#: builtin/fsck.c
-#, c-format
-msgid "%s: object missing"
-msgstr "%s: hi manca l'objecte"
-
-#: builtin/fsck.c
-#, c-format
-msgid "invalid parameter: expected sha1, got '%s'"
-msgstr "paràmetre no vàlid: s'esperava sha1, s'ha obtingut «%s»"
-
 #: builtin/fsmonitor--daemon.c
 msgid "git fsmonitor--daemon start [<options>]"
 msgstr "git fsmonitor--daemon start [<opcions>]"
@@ -8724,6 +9665,10 @@
 "desactivat"
 
 #: builtin/gc.c
+msgid "failed to perform geometric repack"
+msgstr "no s'ha pogut fer el reempaquetament geomètric"
+
+#: builtin/gc.c
 #, c-format
 msgid "task '%s' failed"
 msgstr "la tasca «%s» ha fallat"
@@ -8735,6 +9680,11 @@
 
 #: builtin/gc.c
 #, c-format
+msgid "unknown maintenance strategy: '%s'"
+msgstr "estratègia de manteniment desconeguda: «%s»"
+
+#: builtin/gc.c
+#, c-format
 msgid "'%s' is not a valid task"
 msgstr "«%s» no és una tasca vàlida"
 
@@ -8882,7 +9832,7 @@
 "then try again. If it still fails, a git-maintenance(1) process may have\n"
 "crashed in this repository earlier: remove the file manually to continue."
 msgstr ""
-"No s'ha pogut crear «%s.lock»: %s.\n"
+"no s'ha pogut crear «%s.lock»: %s.\n"
 "\n"
 "Sembla que un altre procés git-maintenance(1) s'està executant en aquest\n"
 "repositori. Assegureu-vos que no hi ha cap altre proés de manteniment en "
@@ -8917,6 +9867,10 @@
 msgstr "no s'ha pogut afegir un repositori a la configuració global"
 
 #: builtin/gc.c
+msgid "check a specific task"
+msgstr "comprova una tasca específica"
+
+#: builtin/gc.c
 msgid "git maintenance <subcommand> [<options>]"
 msgstr "git maintenance <subordre> [<opcions>]"
 
@@ -9373,13 +10327,199 @@
 msgid "'git help config' for more information"
 msgstr "«git help config» per a més informació"
 
+#: builtin/history.c
+msgid "git history reword <commit> [--dry-run] [--update-refs=(branches|head)]"
+msgstr ""
+"git history reword <comissió> [--dry-run] [--update-refs=(branches|head)]"
+
+#: builtin/history.c
+msgid ""
+"git history split <commit> [--dry-run] [--update-refs=(branches|head)] [--] "
+"[<pathspec>...]"
+msgstr ""
+"git history split <comissió> [--dry-run] [--update-refs=(branches|head)] "
+"[--] [<especificació-camí>...]"
+
+#: builtin/history.c
+#, c-format
+msgid ""
+"Please enter the commit message for the %s changes. Lines starting\n"
+"with '%s' will be ignored, and an empty message aborts the commit.\n"
+msgstr ""
+"Introduïu el missatge de comissió dels canvis %s.\n"
+"S'ignoraran les línies que comencin amb «%s». Un missatge de\n"
+"comissió buit avorta la comissió.\n"
+
+#: builtin/history.c
+#, c-format
+msgid "Aborting commit as launching the editor failed.\n"
+msgstr "S'avorta la comissió perquè no s'ha pogut iniciar l'editor.\n"
+
+#: builtin/history.c
+#, c-format
+msgid "unable to parse parent commit %s"
+msgstr "no s'ha pogut analitzar la comissió pare %s"
+
+#: builtin/history.c
+#, c-format
+msgid "%s expects one of 'branches' or 'head'"
+msgstr "%s esperava o bé «branches» o bé «head»"
+
+#: builtin/history.c replay.c
+msgid "error preparing revisions"
+msgstr "s'ha produït un error en preparar les revisions"
+
+#: builtin/history.c replay.c
+msgid "replaying merge commits is not supported yet!"
+msgstr "encara no s'admet la repetició de les comissions de fusió!"
+
+#: builtin/history.c
+msgid "cannot look up HEAD"
+msgstr "no s'ha pogut trobar HEAD"
+
+#: builtin/history.c
+msgid "cannot determine descendance"
+msgstr "no es pot determinar la descendència"
+
+#: builtin/history.c
+msgid ""
+"rewritten commit must be an ancestor of HEAD when using --update-refs=head"
+msgstr ""
+"la comissió reescrita ha de ser un avantpassat de HEAD quan s'utilitza --"
+"update-refs=head"
+
+#: builtin/history.c builtin/replay.c
+#, c-format
+msgid "failed to begin ref transaction: %s"
+msgstr "no s'ha pogut iniciar la transacció de referència %s"
+
+#: builtin/history.c builtin/replay.c
+#, c-format
+msgid "failed to update ref '%s': %s"
+msgstr "no s'ha pogut actualitzat la referència «%s»: %s"
+
+#: builtin/history.c builtin/replay.c
+#, c-format
+msgid "failed to commit ref transaction: %s"
+msgstr "no s'ha pogut cometre la transacció de referència: %s"
+
+#: builtin/history.c
+msgid "control which refs should be updated"
+msgstr "controla quines referències s'haurien d'actualitzar"
+
+#: builtin/history.c
+msgid "perform a dry-run without updating any refs"
+msgstr "fes una prova dry-run sense actualitzar cap referència"
+
+#: builtin/history.c
+msgid "command expects a single revision"
+msgstr "l'ordre espera una única revisió"
+
+#: builtin/history.c
+#, c-format
+msgid "commit cannot be found: %s"
+msgstr "no s'ha pogut trobar la comissió %s"
+
+#: builtin/history.c
+msgid "failed writing reworded commit"
+msgstr "no s'ha pogut escriure la comissió reformulada"
+
+#: builtin/history.c
+msgid "failed replaying descendants"
+msgstr "no s'han pogut reproduir els descendents"
+
+#: builtin/history.c
+msgid "unable to populate index with tree"
+msgstr "no s'ha pogut emplenar l'índex amb l'arbre"
+
+#: builtin/history.c
+msgid "unable to acquire index lock"
+msgstr "no puc adquirir un blocatge d'índex"
+
+#: builtin/history.c
+msgid "failed reading temporary index"
+msgstr "no s'ha pogut llegir l'índex temporal"
+
+#: builtin/history.c
+msgid "failed split tree"
+msgstr "no s'ha pogut dividir l'arbre"
+
+#: builtin/history.c
+msgid "split commit is empty"
+msgstr "la comissió dividida és buida"
+
+#: builtin/history.c
+msgid "split commit tree matches original commit"
+msgstr "l'arbre de comissions dividit coincideix amb la comissió original"
+
+#: builtin/history.c
+msgid "failed writing first commit"
+msgstr "no s'ha pogut fer la primera comissió"
+
+#: builtin/history.c
+msgid "failed writing second commit"
+msgstr "no s'ha pogut fer la segona comissió"
+
+#: builtin/history.c
+msgid "control ref update behavior"
+msgstr "controlar el comportament de l'actualització de referències"
+
+#: builtin/history.c
+msgid "command expects a committish"
+msgstr "l'ordre espera una comissió o similar"
+
+#: builtin/history.c
+msgid "cannot split up merge commit"
+msgstr "no es pot dividir la comissió de fusió"
+
 #: builtin/hook.c
 msgid ""
-"git hook run [--ignore-missing] [--to-stdin=<path>] <hook-name> [-- <hook-"
-"args>]"
+"git hook run [--allow-unknown-hook-name] [--ignore-missing] [--to-"
+"stdin=<path>] <hook-name> [-- <hook-args>]"
 msgstr ""
-"git hook run [--ignore-missing] [--to-stdin=<camí>] <hook-name> [-- <hook-"
-"args>]"
+"git hook run [--allow-unknown-hook-name] [--ignore-missing] [--to-"
+"stdin=<camí>] <nom-lligam> [-- <arguments-lligam>]"
+
+#: builtin/hook.c
+msgid ""
+"git hook list [--allow-unknown-hook-name] [-z] [--show-scope] <hook-name>"
+msgstr ""
+"git hook list [--allow-unknown-hook-name] [-z] [--show-scope] <nom-lligam>"
+
+#: builtin/hook.c
+msgid "use NUL as line terminator"
+msgstr "cal usar NUL com a terminador de línia"
+
+#: builtin/hook.c
+msgid "show the config scope that defined each hook"
+msgstr "mostra l'abast de configuració que va definir cada lligam"
+
+#: builtin/hook.c
+msgid "allow running a hook with a non-native hook name"
+msgstr "permet l'execució d'un lligam amb un nom de lligam no nadiu"
+
+#: builtin/hook.c
+msgid "you must specify a hook event name to list"
+msgstr "heu d'especificar un nom d'esdeveniment de lligam per a llistar"
+
+#: builtin/hook.c
+#, c-format
+msgid ""
+"unknown hook event '%s';\n"
+"use --allow-unknown-hook-name to allow non-native hook names"
+msgstr ""
+"esdeveniment de lligam desconegut «%s»;\n"
+"utilitzeu --allow-unknown-hook-name per a permetre els noms no nadius de "
+"lligam"
+
+#: builtin/hook.c
+#, c-format
+msgid "no hooks found for event '%s'"
+msgstr "no s'han trobat lligams per a l'esdeveniment «%s»"
+
+#: builtin/hook.c
+msgid "hook from hookdir"
+msgstr "lligam del directori de lligams"
 
 #: builtin/hook.c
 msgid "silently ignore missing requested <hook-name>"
@@ -9507,6 +10647,11 @@
 
 #: builtin/index-pack.c
 #, c-format
+msgid "invalid %s"
+msgstr "%s no vàlid"
+
+#: builtin/index-pack.c
+#, c-format
 msgid "Not all child objects of %s are reachable"
 msgstr "No tots els objectes fills de %s són abastables"
 
@@ -9686,6 +10831,10 @@
 msgid "--verify with no packfile name given"
 msgstr "s'ha donat --verify sense nom de fitxer de paquet"
 
+#: builtin/index-pack.c
+msgid "cannot perform queued object checks outside of a repository"
+msgstr "no es pot fer comprovacions d'objectes encuats fora d'un repositori"
+
 #: builtin/index-pack.c builtin/unpack-objects.c
 msgid "fsck error in pack objects"
 msgstr "error fsck als objectes del paquet"
@@ -9760,28 +10909,9 @@
 "                       [--parse] [<file>...]"
 msgstr ""
 "git interpret-trailers [--in-place] [--trim-empty]\n"
-"                       [(--trailer (<clau>|<alies-clau>)[(=|:)<valor>])...]\n"
+"                       [(--trailer (<clau>|<àlies-clau>)[(=|:)<valor>])...]\n"
 "                       [--parse] [<fitxer>...]"
 
-#: builtin/interpret-trailers.c wrapper.c
-#, c-format
-msgid "could not stat %s"
-msgstr "no s'ha pogut fer stat a %s"
-
-#: builtin/interpret-trailers.c
-#, c-format
-msgid "file %s is not a regular file"
-msgstr "el fitxer %s no és un fitxer regular"
-
-#: builtin/interpret-trailers.c
-#, c-format
-msgid "file %s is not writable by user"
-msgstr "el fitxer %s no és gravable per l'usuari"
-
-#: builtin/interpret-trailers.c
-msgid "could not open temporary file"
-msgstr "no s'ha pogut obrir el fitxer temporal"
-
 #: builtin/interpret-trailers.c
 #, c-format
 msgid "could not read input file '%s'"
@@ -9793,6 +10923,11 @@
 
 #: builtin/interpret-trailers.c
 #, c-format
+msgid "could not write to temporary file '%s'"
+msgstr "no s'ha pogut escriure al fitxer temporal «%s»"
+
+#: builtin/interpret-trailers.c trailer.c
+#, c-format
 msgid "could not rename temporary file to %s"
 msgstr "no s'ha pogut canviar el nom del fitxer temporal a %s"
 
@@ -9825,8 +10960,8 @@
 msgstr "mostra només els «trailer»"
 
 #: builtin/interpret-trailers.c
-msgid "do not apply trailer.* configuration variables"
-msgstr "no apliquis les variables de configuració trailer.*"
+msgid "do not apply trailer.<key-alias> configuration variables"
+msgstr "no apliquis les variables de configuració trailer.<àlies-clau>"
 
 #: builtin/interpret-trailers.c
 msgid "reformat multiline trailer values as single-line values"
@@ -9853,6 +10988,46 @@
 msgid "no input file given for in-place editing"
 msgstr "no s'ha donat cap fitxer d'entrada per a edició in situ"
 
+#: builtin/last-modified.c
+msgid "last-modified can only operate on one commit at a time"
+msgstr "last-modified només pot operar sobre una comissió cada vegada"
+
+#: builtin/last-modified.c
+#, c-format
+msgid "revision argument '%s' is a %s, not a commit-ish"
+msgstr "l'argument de revisió «%s» és un %s, no una comissió o similar"
+
+#: builtin/last-modified.c
+#, c-format
+msgid "unknown last-modified argument: %s"
+msgstr "argument de last-modified desconegut %s"
+
+#: builtin/last-modified.c
+msgid ""
+"git last-modified [--recursive] [--show-trees] [--max-depth=<depth>] [-z]\n"
+"                  [<revision-range>] [[--] <pathspec>...]"
+msgstr ""
+"git last-modified [--recursive] [--show-trees] [--max-depth=<profunditat>] [-"
+"z]\n"
+"                  [<rang-revisions>] [[--] <especificació-camí>...]"
+
+#: builtin/last-modified.c builtin/ls-tree.c
+msgid "recurse into subtrees"
+msgstr "inclou recursivament als subarbres"
+
+#: builtin/last-modified.c
+msgid "show tree entries when recursing into subtrees"
+msgstr ""
+"mostra les entrades d'arbre quan es recorren els subarbres recursivament"
+
+#: builtin/last-modified.c diff.c
+msgid "maximum tree depth to recurse"
+msgstr "profunditat màxima d'arbre per a fer recursió"
+
+#: builtin/last-modified.c
+msgid "lines are separated with NUL character"
+msgstr "les línies se separen amb el caràcter NUL"
+
 #: builtin/log.c
 msgid "git log [<options>] [<revision-range>] [[--] <path>...]"
 msgstr "git log [<opcions>] [<rang-de-revisions>] [[--] <camí>...]"
@@ -9903,6 +11078,22 @@
 msgstr "-L<rang>:<fitxer> no es pot usar amb una especificació de camí"
 
 #: builtin/log.c
+msgid ""
+"\n"
+"hint: You can replace 'git whatchanged <opts>' with:\n"
+"hint:\tgit log <opts> --raw --no-merges\n"
+"hint: Or make an alias:\n"
+"hint:\tgit config set --global alias.whatchanged 'log --raw --no-merges'\n"
+"\n"
+msgstr ""
+"\n"
+"consell: Podeu reemplaçar 'git whatchanged <opts>' amb:\n"
+"consell:\tgit log <opts> --raw --no-merges\n"
+"consell: O creant un àlies:\n"
+"hint:\tgit config set --global alias.whatchanged 'log --raw --no-merges'\n"
+"\n"
+
+#: builtin/log.c
 #, c-format
 msgid "git show %s: bad file"
 msgstr "git show %s: fitxer incorrecte"
@@ -9926,6 +11117,20 @@
 msgid "format.headers without value"
 msgstr "format.headers sense valor"
 
+#: builtin/log.c config.c
+#, c-format
+msgid "bad boolean config value '%s' for '%s'"
+msgstr "valor de configuració booleà erroni «%s» per a «%s»"
+
+#: builtin/log.c
+#, c-format
+msgid ""
+"'%s' used to accept any value and treat that as 'true'.\n"
+"Now it only accepts boolean values, like what '%s' does.\n"
+msgstr ""
+"'%s' solia acceptar qualsevol valor i el tractava com a «true».\n"
+"Ara sols accepta valors booleans, com fa «%s».\n"
+
 #: builtin/log.c
 #, c-format
 msgid "cannot open patch file %s"
@@ -9954,6 +11159,11 @@
 
 #: builtin/log.c
 #, c-format
+msgid "'%s' is not a valid format string"
+msgstr "«%s» no és una cadena de format vàlida"
+
+#: builtin/log.c
+#, c-format
 msgid "insane in-reply-to: %s"
 msgstr "in-reply-to boig: %s"
 
@@ -10033,6 +11243,16 @@
 msgstr "genera una carta de presentació"
 
 #: builtin/log.c
+msgid "format-spec"
+msgstr "especificació de format"
+
+#: builtin/log.c
+msgid "format spec used for the commit list in the cover letter"
+msgstr ""
+"especificació de format utilitzada per a la llista de comissions en la carta "
+"de presentació"
+
+#: builtin/log.c
 msgid "use simple number sequence for output file names"
 msgstr "usa una seqüència de números per als noms dels fitxers de sortida"
 
@@ -10480,10 +11700,6 @@
 msgstr "mostra només els arbres"
 
 #: builtin/ls-tree.c
-msgid "recurse into subtrees"
-msgstr "inclou recursivament als subarbres"
-
-#: builtin/ls-tree.c
 msgid "show trees when recursing"
 msgstr "mostra els arbres quan es treballa recursivament"
 
@@ -10623,13 +11839,6 @@
 "git merge-file [<opcions>] [-L <nom1> [-L <original> [-L <nom2>]]] <fitxer1> "
 "<fitxer-original> <fitxer2>"
 
-#: builtin/merge-file.c diff.c
-msgid ""
-"option diff-algorithm accepts \"myers\", \"minimal\", \"patience\" and "
-"\"histogram\""
-msgstr ""
-"l'opció diff-algorithm accepta «myers», «minimal», «patience» i «histogram»"
-
 #: builtin/merge-file.c
 msgid "send results to standard output"
 msgstr "envia els resultats a la sortida estàndard"
@@ -10646,14 +11855,6 @@
 msgid "use a zealous diff3 based merge"
 msgstr "usa una fusió basada en zealous diff3"
 
-#: builtin/merge-file.c diff.c
-msgid "<algorithm>"
-msgstr "<algorisme>"
-
-#: builtin/merge-file.c diff.c
-msgid "choose a diff algorithm"
-msgstr "trieu un algorisme per al diff"
-
 #: builtin/merge-file.c
 msgid "for conflicts, use this marker size"
 msgstr "en conflictes, usa aquesta mida de marcador"
@@ -10677,11 +11878,6 @@
 
 #: builtin/merge-recursive.c
 #, c-format
-msgid "unknown option %s"
-msgstr "opció desconeguda %s"
-
-#: builtin/merge-recursive.c
-#, c-format
 msgid "could not parse object '%s'"
 msgstr "no s'ha pogut analitzar l'objecte «%s»"
 
@@ -10901,11 +12097,6 @@
 msgstr "l'«stash» ha fallat"
 
 #: builtin/merge.c
-#, c-format
-msgid "not a valid object: %s"
-msgstr "no és un objecte vàlid: %s"
-
-#: builtin/merge.c
 msgid "read-tree failed"
 msgstr "read-tree ha fallat"
 
@@ -11204,11 +12395,21 @@
 
 #: builtin/multi-pack-index.c
 msgid ""
-"git multi-pack-index [<options>] write [--preferred-pack=<pack>][--refs-"
-"snapshot=<path>]"
+"git multi-pack-index [<options>] write [--preferred-pack=<pack>]\n"
+"  [--[no-]bitmap] [--[no-]incremental] [--[no-]stdin-packs]\n"
+"  [--refs-snapshot=<path>]"
 msgstr ""
-"git multi-pack-index [<opcions>] write [--preferred-pack=<paquet>][--refs-"
-"snapshot=<camí>]"
+"git multi-pack-index [<opcions>] write [--preferred-pack=<paquet>]\n"
+"  [--[no-]bitmap] [--[no-]incremental] [--[no-]stdin-packs]\n"
+"  [--refs-snapshot=<camí>]"
+
+#: builtin/multi-pack-index.c
+msgid ""
+"git multi-pack-index [<options>] compact [--[no-]incremental]\n"
+"  [--[no-]bitmap] <from> <to>"
+msgstr ""
+"git multi-pack-index [<opcions>] compact [--[no-]incremental]\n"
+"  [--[no-]bitmap] <des-de> <cap-a>"
 
 #: builtin/multi-pack-index.c
 msgid "git multi-pack-index [<options>] verify"
@@ -11259,6 +12460,20 @@
 "instantània de referències per a seleccionar les comissions de mapa de bits"
 
 #: builtin/multi-pack-index.c
+#, c-format
+msgid "could not find MIDX: %s"
+msgstr "no s'ha pogut trobar el MIDX: %s"
+
+#: builtin/multi-pack-index.c
+msgid "MIDX compaction endpoints must be unique"
+msgstr "Els punts finals de compactació MIDX han de ser únics"
+
+#: builtin/multi-pack-index.c
+#, c-format
+msgid "MIDX %s must be an ancestor of %s"
+msgstr "MIDX %s ha de ser un avantpassat de %s"
+
+#: builtin/multi-pack-index.c
 msgid ""
 "during repack, collect pack-files of smaller size into a batch that is "
 "larger than this size"
@@ -11372,7 +12587,7 @@
 msgid "Renaming %s to %s\n"
 msgstr "S'està canviant el nom de %s a %s\n"
 
-#: builtin/mv.c builtin/remote.c
+#: builtin/mv.c
 #, c-format
 msgid "renaming '%s' failed"
 msgstr "el canvi del nom de «%s» ha fallat"
@@ -11436,8 +12651,8 @@
 "| -C) <object>] [<object>] [-e]"
 msgstr ""
 "git notes [--ref <referència-notes>] add [-f] [--allow-empty] [--"
-"[no-]separator|--separator=<salt-paràgraf>] [--[no-]stripspace] [-m <msg> | -"
-"F <fitxer> | (-c | -C) <objecte>] [<objecte>]"
+"[no-]separator|--separator=<salt-paràgraf>] [--[no-]stripspace] [-m <msg> | "
+"-F <fitxer> | (-c | -C) <objecte>] [<objecte>]"
 
 #: builtin/notes.c
 msgid "git notes [--ref <notes-ref>] copy [-f] <from-object> <to-object>"
@@ -11451,8 +12666,8 @@
 "| -C) <object>] [<object>] [-e]"
 msgstr ""
 "git notes [--ref <referència-notes>] append [--allow-empty] [--"
-"[no-]separator|--separator=<salt-paràgraf>] [--[no-]stripspace] [-m <msg> | -"
-"F <fitxer> | (-c | -C) <objecte>] [<objecte>] [-e]"
+"[no-]separator|--separator=<salt-paràgraf>] [--[no-]stripspace] [-m <msg> | "
+"-F <fitxer> | (-c | -C) <objecte>] [<objecte>] [-e]"
 
 #: builtin/notes.c
 msgid "git notes [--ref <notes-ref>] edit [--allow-empty] [<object>]"
@@ -11944,7 +13159,7 @@
 msgid "delta base offset out of bound for %s"
 msgstr "desplaçament base de diferències fora dels límits per a %s"
 
-#: builtin/pack-objects.c
+#: builtin/pack-objects.c builtin/repo.c
 msgid "Counting objects"
 msgstr "S'estan comptant els objectes"
 
@@ -12042,6 +13257,13 @@
 
 #: builtin/pack-objects.c
 #, c-format
+msgid "packfile %s is a promisor but --exclude-promisor-objects was given"
+msgstr ""
+"el fitxer de paquets %s és un «promissor» però s'ha indicat --exclude-"
+"promisor-objects"
+
+#: builtin/pack-objects.c
+#, c-format
 msgid "packfile %s cannot be accessed"
 msgstr "no es pot accedir al fitxer de paquet %s"
 
@@ -12075,10 +13297,6 @@
 "s'esperava un identificador d'objecte, s'ha rebut brossa:\n"
 " %s"
 
-#: builtin/pack-objects.c reachable.c
-msgid "could not load cruft pack .mtimes"
-msgstr "no s'ha pogut carregar superflus del paquet superflu"
-
 #: builtin/pack-objects.c
 msgid "cannot open pack index"
 msgstr "no s'ha pogut obrir l'índex del paquet"
@@ -12371,44 +13589,20 @@
 "diferències), paquets reusats %<PRIu32> (de %<PRIuMAX>)"
 
 #: builtin/pack-refs.c
-msgid ""
-"git pack-refs [--all] [--no-prune] [--auto] [--include <pattern>] [--exclude "
-"<pattern>]"
-msgstr ""
-"git pack-refs [--all] [--no-prune] [--auto] [--include <patró>] [--exclude "
-"<patró>]"
-
-#: builtin/pack-refs.c
-msgid "pack everything"
-msgstr "empaqueta-ho tot"
-
-#: builtin/pack-refs.c
-msgid "prune loose refs (default)"
-msgstr "poda les referències soltes (per defecte)"
-
-#: builtin/pack-refs.c
-msgid "auto-pack refs as needed"
-msgstr "auto-empaqueta referències si cal"
-
-#: builtin/pack-refs.c
-msgid "references to include"
-msgstr "referències a incloure"
-
-#: builtin/pack-refs.c
-msgid "references to exclude"
-msgstr "referències a excloure"
+msgid "git pack-refs "
+msgstr "git pack-refs "
 
 #: builtin/patch-id.c
 msgid "git patch-id [--stable | --unstable | --verbatim]"
 msgstr "git patch-id [--stable | --unstable | --verbatim]"
 
 #: builtin/patch-id.c
-msgid "use the unstable patch-id algorithm"
-msgstr "utilitza l'algorisme inestable de patch-id"
+msgid "use the unstable patch ID algorithm"
+msgstr "usa l'algorisme inestable de patch-id"
 
 #: builtin/patch-id.c
-msgid "use the stable patch-id algorithm"
-msgstr "utilitza l'algorisme estable de patch-id"
+msgid "use the stable patch ID algorithm"
+msgstr "usa l'algorisme estable de patch-id"
 
 #: builtin/patch-id.c
 msgid "don't strip whitespace from the patch"
@@ -12440,50 +13634,6 @@
 msgstr "git pull [<opcions>] [<repositori> [<especificació-referència>...]]"
 
 #: builtin/pull.c
-msgid "control for recursive fetching of submodules"
-msgstr "controla l'obtenció recursiva de submòduls"
-
-#: builtin/pull.c
-msgid "Options related to merging"
-msgstr "Opcions relacionades amb fusionar"
-
-#: builtin/pull.c
-msgid "incorporate changes by rebasing rather than merging"
-msgstr "incorpora els canvis fent «rebase» en lloc de fusionar"
-
-#: builtin/pull.c builtin/revert.c
-msgid "allow fast-forward"
-msgstr "permet l'avanç ràpid"
-
-#: builtin/pull.c
-msgid "control use of pre-merge-commit and commit-msg hooks"
-msgstr "controla l'ús dels lligams pre-merge-commit i commit-msg"
-
-#: builtin/pull.c parse-options.h
-msgid "automatically stash/stash pop before and after"
-msgstr "fes «stash» i «stash pop» automàticament abans i després"
-
-#: builtin/pull.c
-msgid "Options related to fetching"
-msgstr "Opcions relacionades amb obtenir"
-
-#: builtin/pull.c
-msgid "force overwrite of local branch"
-msgstr "força la sobreescriptura de la branca local"
-
-#: builtin/pull.c
-msgid "number of submodules pulled in parallel"
-msgstr "nombre de submòduls baixats en paral·lel"
-
-#: builtin/pull.c parse-options.h
-msgid "use IPv4 addresses only"
-msgstr "usa només adreces IPv4"
-
-#: builtin/pull.c parse-options.h
-msgid "use IPv6 addresses only"
-msgstr "usa només adreces IPv6"
-
-#: builtin/pull.c
 msgid ""
 "There is no candidate for rebasing against among the refs that you just "
 "fetched."
@@ -12605,6 +13755,50 @@
 "per defecte de la configuració en aquesta execució.\n"
 
 #: builtin/pull.c
+msgid "control for recursive fetching of submodules"
+msgstr "controla l'obtenció recursiva de submòduls"
+
+#: builtin/pull.c
+msgid "Options related to merging"
+msgstr "Opcions relacionades amb fusionar"
+
+#: builtin/pull.c
+msgid "incorporate changes by rebasing rather than merging"
+msgstr "incorpora els canvis fent «rebase» en lloc de fusionar"
+
+#: builtin/pull.c builtin/revert.c
+msgid "allow fast-forward"
+msgstr "permet l'avanç ràpid"
+
+#: builtin/pull.c
+msgid "control use of pre-merge-commit and commit-msg hooks"
+msgstr "controla l'ús dels lligams pre-merge-commit i commit-msg"
+
+#: builtin/pull.c parse-options.h
+msgid "automatically stash/stash pop before and after"
+msgstr "fes «stash» i «stash pop» automàticament abans i després"
+
+#: builtin/pull.c
+msgid "Options related to fetching"
+msgstr "Opcions relacionades amb obtenir"
+
+#: builtin/pull.c
+msgid "force overwrite of local branch"
+msgstr "força la sobreescriptura de la branca local"
+
+#: builtin/pull.c
+msgid "number of submodules pulled in parallel"
+msgstr "nombre de submòduls baixats en paral·lel"
+
+#: builtin/pull.c parse-options.h
+msgid "use IPv4 addresses only"
+msgstr "usa només adreces IPv4"
+
+#: builtin/pull.c parse-options.h
+msgid "use IPv6 addresses only"
+msgstr "usa només adreces IPv6"
+
+#: builtin/pull.c
 msgid "Updating an unborn branch with changes added to the index."
 msgstr ""
 "S'està actualitzant una branca no nascuda amb canvis afegits a l'índex."
@@ -13007,6 +14201,11 @@
 msgstr "git range-diff [<opcions>] <base> <old-tip> <new-tip>"
 
 #: builtin/range-diff.c
+#, c-format
+msgid "invalid max-memory value: %s"
+msgstr "valor invàlid de max-memory %s"
+
+#: builtin/range-diff.c
 msgid "use simple diff colors"
 msgstr "utilitza colors simples de diff"
 
@@ -13019,6 +14218,14 @@
 msgstr "passa-ho a «git log»"
 
 #: builtin/range-diff.c
+msgid "size"
+msgstr "grandària"
+
+#: builtin/range-diff.c
+msgid "maximum memory for cost matrix (default 4G)"
+msgstr "memòria màxima per a la matriu de cost (per defecte 4G)"
+
+#: builtin/range-diff.c
 msgid "only emit output related to the first range"
 msgstr "emet només la sortida relacionada amb el primer interval"
 
@@ -13231,8 +14438,8 @@
 #: builtin/rebase.c
 #, c-format
 msgid ""
-"unrecognized empty type '%s'; valid values are \"drop\", \"keep\", and \"stop"
-"\"."
+"unrecognized empty type '%s'; valid values are \"drop\", \"keep\", and "
+"\"stop\"."
 msgstr ""
 "tipus buit «%s» no reconegut; els valors vàlids són \"drop\", \"keep\" i "
 "\"stop\"."
@@ -13698,16 +14905,12 @@
 msgstr "git reflog list"
 
 #: builtin/reflog.c
-msgid ""
-"git reflog expire [--expire=<time>] [--expire-unreachable=<time>]\n"
-"                  [--rewrite] [--updateref] [--stale-fix]\n"
-"                  [--dry-run | -n] [--verbose] [--all [--single-worktree] | "
-"<refs>...]"
-msgstr ""
-"git reflog expire [--expire=<time>] [--expire-unreachable=<time>]\n"
-"                  [--rewrite] [--updateref] [--stale-fix]\n"
-"                  [--dry-run | -n] [--verbose] [--all [--single-worktree] | "
-"<refs>...]"
+msgid "git reflog exists <ref>"
+msgstr "git reflog exists <referència>"
+
+#: builtin/reflog.c
+msgid "git reflog write <ref> <old-oid> <new-oid> <message>"
+msgstr "git reflog write <referència> <oid-antic> <oid-nou> <missatge>"
 
 #: builtin/reflog.c
 msgid ""
@@ -13719,14 +14922,22 @@
 "<referència>@{<especificador>}..."
 
 #: builtin/reflog.c
-msgid "git reflog exists <ref>"
-msgstr "git reflog exists <referència>"
-
-#: builtin/reflog.c
 msgid "git reflog drop [--all [--single-worktree] | <refs>...]"
 msgstr "git reflog drop [--all [--single-worktree] | <refs>...]"
 
 #: builtin/reflog.c
+msgid ""
+"git reflog expire [--expire=<time>] [--expire-unreachable=<time>]\n"
+"                  [--rewrite] [--updateref] [--stale-fix]\n"
+"                  [--dry-run | -n] [--verbose] [--all [--single-worktree] | "
+"<refs>...]"
+msgstr ""
+"git reflog expire [--expire=<time>] [--expire-unreachable=<time>]\n"
+"                  [--rewrite] [--updateref] [--stale-fix]\n"
+"                  [--dry-run | -n] [--verbose] [--all [--single-worktree] | "
+"<refs>...]"
+
+#: builtin/reflog.c
 #, c-format
 msgid "invalid timestamp '%s' given to '--%s'"
 msgstr "marca de temps «%s» donada a «--%s» no és vàlida"
@@ -13818,6 +15029,46 @@
 msgid "references specified along with --all"
 msgstr "referències especificades conjuntament amb --all"
 
+#: builtin/reflog.c
+#, c-format
+msgid "invalid reference name: %s"
+msgstr "nom de referència no vàlid: %s"
+
+#: builtin/reflog.c
+#, c-format
+msgid "invalid old object ID: '%s'"
+msgstr "ID invàlid d'objecte antic: «%s»"
+
+#: builtin/reflog.c
+#, c-format
+msgid "old object '%s' does not exist"
+msgstr "l'objecte antic «%s» no existeix"
+
+#: builtin/reflog.c
+#, c-format
+msgid "invalid new object ID: '%s'"
+msgstr "ID invàlid d'objecte nou: «%s»"
+
+#: builtin/reflog.c
+#, c-format
+msgid "new object '%s' does not exist"
+msgstr "l'objecte nou «%s» no existeix"
+
+#: builtin/reflog.c
+#, c-format
+msgid "cannot start transaction: %s"
+msgstr "no es pot iniciar la transacció: %s"
+
+#: builtin/reflog.c
+#, c-format
+msgid "cannot queue reflog update: %s"
+msgstr "no es pot encuar l'actualització del registre de referències: %s"
+
+#: builtin/reflog.c
+#, c-format
+msgid "cannot commit reflog update: %s"
+msgstr "no es pot cometre l'actualització del registre de referències: %s"
+
 #: builtin/refs.c
 msgid "git refs migrate --ref-format=<format> [--no-reflog] [--dry-run]"
 msgstr "git refs migrate --ref-format=<format> [--no-reflog] [--dry-run]"
@@ -13827,6 +15078,14 @@
 msgstr "git refs verify [--strict] [--verbose]"
 
 #: builtin/refs.c
+msgid "git refs exists <ref>"
+msgstr "git refs exists <referència>"
+
+#: builtin/refs.c
+msgid "git refs optimize "
+msgstr "git refs optimize "
+
+#: builtin/refs.c
 msgid "specify the reference format to convert to"
 msgstr "especifiqueu el format de referència al qual voleu convertir"
 
@@ -13855,6 +15114,22 @@
 msgid "'git refs verify' takes no arguments"
 msgstr "'git refs verify' no accepta arguments"
 
+#: builtin/refs.c
+msgid "git refs list "
+msgstr "git refs list "
+
+#: builtin/refs.c
+msgid "'git refs exists' requires a reference"
+msgstr "'git refs exists' requereix una referència"
+
+#: builtin/refs.c builtin/show-ref.c
+msgid "reference does not exist"
+msgstr "la referència no existeix"
+
+#: builtin/refs.c builtin/show-ref.c
+msgid "failed to look up reference"
+msgstr "s'ha produït en cercar la referència"
+
 #: builtin/remote.c
 msgid ""
 "git remote add [-t <branch>] [-m <master>] [-f] [--tags | --no-tags] [--"
@@ -14058,6 +15333,23 @@
 "ara anomena un remot no existent «%s»"
 
 #: builtin/remote.c
+msgid ""
+"The remote you are trying to rename has conflicting references in the\n"
+"new target refspec. This is most likely caused by you trying to nest\n"
+"a remote into itself, e.g. by renaming 'parent' into 'parent/child'\n"
+"or by unnesting a remote, e.g. the other way round.\n"
+"\n"
+"If that is the case, you can address this by first renaming the\n"
+"remote to a different name.\n"
+msgstr ""
+"El remot al qual intenteu canviar el nom té referències en conflicte\n"
+"en el nou refspec de destinació. Això es deu probablement a un intent\n"
+"d'imbricar un remot dins d'ell mateix, p. ex., canviant el nom «pare» per\n"
+"«pare/fill» o al revés, desimbricant un remot.\n"
+"\"\\n\"\n"
+"\"Si és el cas, podeu resoldre-ho canviant primer el nom del remot,\n"
+
+#: builtin/remote.c
 #, c-format
 msgid "No such remote: '%s'"
 msgstr "No existeix el remot «%s»"
@@ -14068,6 +15360,15 @@
 msgstr "No s'ha pogut canviar el nom de la secció de configuració «%s» a «%s»"
 
 #: builtin/remote.c
+msgid "Renaming remote references"
+msgstr "S'està canviant el nom de les referències remotes"
+
+#: builtin/remote.c
+#, c-format
+msgid "queueing remote ref renames failed: %s"
+msgstr "no s'han pogut encuar els canvis de nom de les referències remotes: %s"
+
+#: builtin/remote.c
 #, c-format
 msgid ""
 "Not updating non-default fetch refspec\n"
@@ -14080,18 +15381,9 @@
 "\tActualitzeu la configuració manualment si és necessari."
 
 #: builtin/remote.c
-msgid "Renaming remote references"
-msgstr "S'està canviant el nom de les referències remotes"
-
-#: builtin/remote.c
 #, c-format
-msgid "deleting '%s' failed"
-msgstr "la supressió de «%s» ha fallat"
-
-#: builtin/remote.c
-#, c-format
-msgid "creating '%s' failed"
-msgstr "la creació de «%s» ha fallat"
+msgid "renaming remote refs failed: %s"
+msgstr "el canvi de nom de les referències remotes ha fallat: %s"
 
 #: builtin/remote.c
 msgid ""
@@ -14440,66 +15732,8 @@
 msgstr ""
 "Els reempaquetaments incrementals són incompatibles amb els índexs de mapes "
 "de bits.  Useu\n"
-"--no-write-bitmap-index o inhabiliteu el paràmetre de configuració pack."
-"writeBitmaps."
-
-#: builtin/repack.c
-msgid "could not start pack-objects to repack promisor objects"
-msgstr ""
-"no s'ha pogut iniciar pack-objects per a tornar a empaquetar els objectes "
-"«promisor»"
-
-#: builtin/repack.c
-msgid "failed to feed promisor objects to pack-objects"
-msgstr "no s'ha pogut alimentar pack-objects amb objectes «promisor»"
-
-#: builtin/repack.c
-msgid "repack: Expecting full hex object ID lines only from pack-objects."
-msgstr ""
-"repack: s'esperen només línies amb l'id d'objecte hexadecimal complet des de "
-"pack-objects."
-
-#: builtin/repack.c
-msgid "could not finish pack-objects to repack promisor objects"
-msgstr ""
-"no s'ha pogut finalitzar pack-objects per a tornar a empaquetar els objectes "
-"«promisor»"
-
-#: builtin/repack.c
-#, c-format
-msgid "cannot open index for %s"
-msgstr "no s'ha pogut obrir l'índex per a %s"
-
-#: builtin/repack.c
-#, c-format
-msgid "pack %s too large to consider in geometric progression"
-msgstr ""
-"el paquet %s és massa gran per a considerar-ho en progressió geomètrica"
-
-#: builtin/repack.c
-#, c-format
-msgid "pack %s too large to roll up"
-msgstr "el paquet %s és massa gran per a enrotllar-lo"
-
-#: builtin/repack.c
-#, c-format
-msgid "could not open tempfile %s for writing"
-msgstr "no s'ha pogut obrir el fitxer temporal «%s» per a escriptura"
-
-#: builtin/repack.c
-msgid "could not close refs snapshot tempfile"
-msgstr ""
-"no s'ha pogut tancar el fitxer temporal amb la instantània de referències"
-
-#: builtin/repack.c
-#, c-format
-msgid "could not remove stale bitmap: %s"
-msgstr "no s'ha pogut eliminar el mapa de bits estancat: %s"
-
-#: builtin/repack.c
-#, c-format
-msgid "pack prefix %s does not begin with objdir %s"
-msgstr "el prefix de paquet %s no comença amb objdir %s"
+"--no-write-bitmap-index o inhabiliteu el paràmetre de configuració "
+"pack.writeBitmaps."
 
 #: builtin/repack.c
 msgid "pack everything in a single pack"
@@ -14635,22 +15869,6 @@
 msgid "Nothing new to pack."
 msgstr "Res nou a empaquetar."
 
-#: builtin/repack.c
-#, c-format
-msgid "renaming pack to '%s' failed"
-msgstr "el canvi del nom a «%s» ha fallat"
-
-#: builtin/repack.c
-#, c-format
-msgid "pack-objects did not write a '%s' file for pack %s-%s"
-msgstr ""
-"els objectes de paquet no han escrit a un fitxer «%s» per al paquet %s-%s"
-
-#: builtin/repack.c sequencer.c
-#, c-format
-msgid "could not unlink: %s"
-msgstr "no s'ha pogut desenllaçar: «%s»"
-
 #: builtin/replace.c
 msgid "git replace [-f] <object> <replacement>"
 msgstr "git replace [-f] <objecte> <reemplaçament>"
@@ -14890,67 +16108,49 @@
 msgstr "només es pot especificar un patró amb -l"
 
 #: builtin/replay.c
-msgid "need some commits to replay"
-msgstr "calen algunes comissions per tornar a reproduir"
-
-#: builtin/replay.c
-msgid "all positive revisions given must be references"
-msgstr "totes les revisions positives que s'han donat han de ser referències"
-
-#: builtin/replay.c
-msgid "argument to --advance must be a reference"
-msgstr "l'argument per a --advance ha de ser una referència"
+#, c-format
+msgid "invalid %s value: '%s'"
+msgstr "valor de %s invàlid: «%s»"
 
 #: builtin/replay.c
 msgid ""
-"cannot advance target with multiple sources because ordering would be ill-"
-"defined"
+"(EXPERIMENTAL!) git replay ([--contained] --onto=<newbase> | --"
+"advance=<branch> | --revert=<branch>)\n"
+"[--ref=<ref>] [--ref-action=<mode>] <revision-range>"
 msgstr ""
-"no es pot avançar l'objectiu amb múltiples fonts perquè l'ordenació no "
-"estaria definida correctament"
+"(EXPERIMENTAL!) git replay ([--contained] --onto=<base-nova> | --"
+"advance=<branca> | --revert=<branca>)\n"
+"[--ref=<referència>] [--ref-action=<mode>] <rang-revisions>"
 
 #: builtin/replay.c
-msgid ""
-"cannot implicitly determine whether this is an --advance or --onto operation"
+msgid "update all branches that point at commits in <revision-range>"
 msgstr ""
-"no es pot determinar implícitament si aquesta és una operació --advance o --"
-"onto"
-
-#: builtin/replay.c
-msgid ""
-"cannot advance target with multiple source branches because ordering would "
-"be ill-defined"
-msgstr ""
-"no es pot avançar l'objectiu amb múltiples branques d'origen perquè "
-"l'ordenació no estaria definida correctament"
-
-#: builtin/replay.c
-msgid "cannot implicitly determine correct base for --onto"
-msgstr "no es pot determinar implícitament la base correcta per a --onto"
-
-#: builtin/replay.c
-msgid ""
-"(EXPERIMENTAL!) git replay ([--contained] --onto <newbase> | --advance "
-"<branch>) <revision-range>..."
-msgstr ""
-"(EXPERIMENTAL!) git replay ([--contained] --onto <newbase> | --advance "
-"<branch>) <revision-range>..."
-
-#: builtin/replay.c
-msgid "make replay advance given branch"
-msgstr "fes avançar la repetició de la branca donada"
+"actualitza totes les branques que apunten a comissions en <revision-range>"
 
 #: builtin/replay.c
 msgid "replay onto given commit"
 msgstr "torna a reproduir a la comissió donada"
 
 #: builtin/replay.c
-msgid "advance all branches contained in revision-range"
-msgstr "avança totes les branques contingudes a l'interval de revisions"
+msgid "make replay advance given branch"
+msgstr "fes avançar la repetició de la branca donada"
 
 #: builtin/replay.c
-msgid "option --onto or --advance is mandatory"
-msgstr "l'opció --onto o --advance és obligatòria"
+msgid "revert commits onto given branch"
+msgstr "el revert fa «commit» en la branca donada"
+
+#: builtin/replay.c
+msgid "reference to update with result"
+msgstr "referència per a l'actualització amb resultat"
+
+#: builtin/replay.c
+msgid "control ref update behavior (update|print)"
+msgstr ""
+"controlar el comportament de l'actualització de referències (update|print)"
+
+#: builtin/replay.c
+msgid "exactly one of --onto, --advance, or --revert is required"
+msgstr "es requereix exactament una de: --onto, --advance, o --revert"
 
 #: builtin/replay.c
 #, c-format
@@ -14961,23 +16161,130 @@
 "algunes opcions de referència se sobreescriuran de forma forçada com a «%s» "
 "bits a «struct rev_info»"
 
-#: builtin/replay.c
-msgid "error preparing revisions"
-msgstr "s'ha produït un error en preparar les revisions"
+#: builtin/repo.c
+#, c-format
+msgid "key '%s' not found"
+msgstr "no s'ha trobat la clau «%s»."
 
-#: builtin/replay.c
-msgid "replaying down to root commit is not supported yet!"
-msgstr "encara no s'admet la reproducció cap avall en una comissió arrel"
+#: builtin/repo.c
+msgid "--keys can only be used with --format=lines or --format=nul"
+msgstr "--keys sols es pot usar amb --format=lines o --format=nul"
 
-#: builtin/replay.c
-msgid "replaying merge commits is not supported yet!"
-msgstr "encara no s'admet la repetició de les comissions de fusió"
+#: builtin/repo.c
+#, c-format
+msgid "invalid format '%s'"
+msgstr "format invàlid «%s»"
+
+#: builtin/repo.c
+msgid "output format"
+msgstr "format de sortida"
+
+#: builtin/repo.c
+msgid "synonym for --format=nul"
+msgstr "sinònim de --format=nul"
+
+#: builtin/repo.c
+msgid "print all keys/values"
+msgstr "imprimeix totes les claus/valors"
+
+#: builtin/repo.c
+msgid "show keys"
+msgstr "mostra les claus"
+
+#: builtin/repo.c
+msgid "--keys cannot be used with a <key> or --all"
+msgstr "--keys no es pot usar amb <key> o --all"
+
+#: builtin/repo.c
+msgid "unsupported output format"
+msgstr "format de sortida no compatible"
+
+#: builtin/repo.c
+msgid "--all and <key> cannot be used together"
+msgstr "--all i <key> no es poden usar simultàniament"
+
+#: builtin/repo.c
+msgid "References"
+msgstr "Referències"
+
+#: builtin/repo.c
+msgid "Count"
+msgstr "Compte"
+
+#: builtin/repo.c
+msgid "Branches"
+msgstr "Branques"
+
+#: builtin/repo.c
+msgid "Tags"
+msgstr "Etiquetes"
+
+#: builtin/repo.c
+msgid "Remotes"
+msgstr "Remots"
+
+#: builtin/repo.c
+msgid "Others"
+msgstr "Altres"
+
+#: builtin/repo.c
+msgid "Reachable objects"
+msgstr "Objectes abastables"
+
+#: builtin/repo.c
+msgid "Commits"
+msgstr "Comissions"
+
+#: builtin/repo.c
+msgid "Trees"
+msgstr "Arbres"
+
+#: builtin/repo.c
+msgid "Blobs"
+msgstr "Blobs"
+
+#: builtin/repo.c
+msgid "Inflated size"
+msgstr "Grandària descomprimida"
+
+#: builtin/repo.c
+msgid "Disk size"
+msgstr "Grandària de disc"
+
+#: builtin/repo.c
+msgid "Largest objects"
+msgstr "Objectes més grans"
+
+#: builtin/repo.c
+msgid "Maximum size"
+msgstr "Mida màxima"
+
+#: builtin/repo.c
+msgid "Maximum parents"
+msgstr "Nombre màxim de pares"
+
+#: builtin/repo.c
+msgid "Maximum entries"
+msgstr "Nombre màxim d'entrades"
+
+#: builtin/repo.c
+msgid "Repository structure"
+msgstr "Estructura del repositori"
+
+#: builtin/repo.c
+msgid "Value"
+msgstr "Valor"
+
+#: builtin/repo.c
+msgid "Counting references"
+msgstr "Comptant referències"
 
 #: builtin/rerere.c
 msgid ""
 "git rerere [clear | forget <pathspec>... | diff | status | remaining | gc]"
 msgstr ""
-"git rerere [clear | forget <pathspec>... | diff | status | remaining | gc]"
+"git rerere [clear | forget <especificació-camí>... | diff | status | "
+"remaining | gc]"
 
 #: builtin/rerere.c
 msgid "register clean resolutions in index"
@@ -15000,7 +16307,7 @@
 
 #: builtin/reset.c
 msgid "git reset [-q] [<tree-ish>] [--] <pathspec>..."
-msgstr "git reset [-q] [<tree-ish>] [--] <pathspec>..."
+msgstr "git reset [-q] [<tree-ish>] [--] <especificació-camí>..."
 
 #: builtin/reset.c
 msgid ""
@@ -15010,7 +16317,7 @@
 
 #: builtin/reset.c
 msgid "git reset --patch [<tree-ish>] [--] [<pathspec>...]"
-msgstr "git reset --patch [<tree-ish>] [--] [<pathspec>...]"
+msgstr "git reset --patch [<tree-ish>] [--] [<especificació-camí>...]"
 
 #: builtin/reset.c
 msgid "mixed"
@@ -15373,7 +16680,7 @@
 msgstr ""
 "git rm [-f | --force] [-n] [-r] [--cached] [--ignore-unmatch]\n"
 "       [--quiet] [--pathspec-from-file=<fitxer> [--pathspec-file-nul]]\n"
-"       [--] [<pathspec>...]"
+"       [--] [<especificació-camí>...]"
 
 #: builtin/rm.c
 msgid ""
@@ -15690,6 +16997,55 @@
 msgid "Unknown hash algorithm"
 msgstr "Algorisme de resum desconegut"
 
+#: builtin/show-index.c
+msgid "assuming SHA-1; use --object-format to override"
+msgstr "s'assumeix SHA-1; useu --object-format per a sobreescriure-ho"
+
+#: builtin/show-index.c
+msgid "unable to read header"
+msgstr "no s'ha pogut llegir la capçalera %s"
+
+#: builtin/show-index.c
+msgid "unknown index version"
+msgstr "versió d'índex desconeguda"
+
+#: builtin/show-index.c
+msgid "unable to read index"
+msgstr "no es pot llegir el fitxer d'índex"
+
+#: builtin/show-index.c
+msgid "corrupt index file"
+msgstr "fitxer d'índex malmès"
+
+#: builtin/show-index.c
+#, c-format
+msgid "unable to read entry %u/%u"
+msgstr "no s'ha pogut llegir l'entrada %u/%u"
+
+#: builtin/show-index.c
+#, c-format
+msgid "unable to read sha1 %u/%u"
+msgstr "no s'ha pogut llegir el sha1 %u/%u"
+
+#: builtin/show-index.c
+#, c-format
+msgid "unable to read crc %u/%u"
+msgstr "no s'ha pogut llegir el crc %u/%u"
+
+#: builtin/show-index.c
+#, c-format
+msgid "unable to read 32b offset %u/%u"
+msgstr "no s'ha pogut llegir el desplaçament de 32b %u/%u"
+
+#: builtin/show-index.c
+msgid "inconsistent 64b offset index"
+msgstr "índex de desplaçament de 64b inconsistent"
+
+#: builtin/show-index.c
+#, c-format
+msgid "unable to read 64b offset %u"
+msgstr "no s'ha pogut llegir el desplaçament de 64b %u"
+
 #: builtin/show-ref.c
 msgid ""
 "git show-ref [--head] [-d | --dereference]\n"
@@ -15719,14 +17075,6 @@
 msgstr "git show-ref --exists <referència>"
 
 #: builtin/show-ref.c
-msgid "reference does not exist"
-msgstr "la referència no existeix"
-
-#: builtin/show-ref.c
-msgid "failed to look up reference"
-msgstr "s'ha produït en cercar la referència"
-
-#: builtin/show-ref.c
 msgid "only show tags (can be combined with --branches)"
 msgstr "mostra només les etiquetes (es pot combinar amb --branches)"
 
@@ -15767,10 +17115,10 @@
 #: builtin/sparse-checkout.c
 msgid ""
 "git sparse-checkout (init | list | set | add | reapply | disable | check-"
-"rules) [<options>]"
+"rules | clean) [<options>]"
 msgstr ""
 "git sparse-checkout (init | list | set | add | reapply | disable | check-"
-"rules) [<opcions>]"
+"rules | clean) [<opcions>]"
 
 #: builtin/sparse-checkout.c
 msgid "this worktree is not sparse"
@@ -15821,11 +17169,6 @@
 msgid "toggle the use of a sparse index"
 msgstr "commuta l'ús d'un índex dispers"
 
-#: builtin/sparse-checkout.c commit-graph.c midx-write.c sequencer.c
-#, c-format
-msgid "unable to create leading directories of %s"
-msgstr "no s'han pogut crear els directoris inicials de «%s»"
-
 #: builtin/sparse-checkout.c
 #, c-format
 msgid "failed to open '%s'"
@@ -15924,6 +17267,40 @@
 "ha d'estar en un sparse-checkout per a tornar a aplicar patrons de dispersió"
 
 #: builtin/sparse-checkout.c
+msgid "report each affected file, not just directories"
+msgstr "informa de cada fitxer afectat, no només dels directoris"
+
+#: builtin/sparse-checkout.c
+msgid "must be in a sparse-checkout to clean directories"
+msgstr "ha d'estar en un sparse-checkout per a netejar directoris"
+
+#: builtin/sparse-checkout.c
+msgid "must be in a cone-mode sparse-checkout to clean directories"
+msgstr ""
+"ha d'estar en un sparse-checkout de mode cònic per a netejar directoris"
+
+#: builtin/sparse-checkout.c
+msgid "for safety, refusing to clean without one of --force or --dry-run"
+msgstr "per seguretat, es refusa netejar sense --force o --dry-run"
+
+#: builtin/sparse-checkout.c
+msgid "failed to read index"
+msgstr "no s'ha pogut llegir l'índex"
+
+#: builtin/sparse-checkout.c
+msgid ""
+"failed to convert index to a sparse index; resolve merge conflicts and try "
+"again"
+msgstr ""
+"no s'ha pogut convertir l'índex a un índex dispers; resoleu els conflictes "
+"de fusió i intenteu-ho de nou"
+
+#: builtin/sparse-checkout.c
+#, c-format
+msgid "failed to remove '%s'"
+msgstr "no s'ha pogut eliminar %s"
+
+#: builtin/sparse-checkout.c
 msgid "error while refreshing working directory"
 msgstr "s'ha produït un error en actualitzar el directori de treball"
 
@@ -15984,19 +17361,19 @@
 
 #: builtin/stash.c
 msgid ""
-"git stash [push [-p | --patch] [-S | --staged] [-k | --[no-]keep-index] [-q "
+"git stash [push] [-p | --patch] [-S | --staged] [-k | --[no-]keep-index] [-q "
 "| --quiet]\n"
 "          [-u | --include-untracked] [-a | --all] [(-m | --message) "
 "<message>]\n"
 "          [--pathspec-from-file=<file> [--pathspec-file-nul]]\n"
-"          [--] [<pathspec>...]]"
+"          [--] [<pathspec>...]"
 msgstr ""
 "git stash [push [-p | --patch] [-S | --staged] [-k | --[no-]keep-index] [-q "
 "| --quiet]\n"
 "          [-u | --include-untracked] [-a | --all] [(-m | --message) "
 "<missatge>]\n"
 "          [--pathspec-from-file=<fitxer> [--pathspec-file-nul]]\n"
-"          [--] [<pathspec>...]]"
+"          [--] [<especificació-camí>...]]"
 
 #: builtin/stash.c
 msgid ""
@@ -16336,6 +17713,10 @@
 msgstr "no s'ha pogut obtenir el gestor del repositori pel submòdul «%s»"
 
 #: builtin/submodule--helper.c
+msgid "git submodule--helper get-default-remote <path>"
+msgstr "git submodule--helper get-default-remote <camí>"
+
+#: builtin/submodule--helper.c
 #, c-format
 msgid "No url found for submodule path '%s' in .gitmodules"
 msgstr "No s'ha trobat cap url per al camí de submòdul «%s» a .gitmodules"
@@ -16379,6 +17760,18 @@
 
 #: builtin/submodule--helper.c
 #, c-format
+msgid ""
+"failed to set a valid default config for 'submodule.%s.gitdir'. Please "
+"ensure it is set, for example by running something like: 'git config "
+"submodule.%s.gitdir .git/modules/%s'"
+msgstr ""
+"no s'ha pogut establir una configuració per defecte vàlida per a 'submodule."
+"%s.gitdir'. Si us plau, assegureu-vos que està establert, per exemple, "
+"executant alguna ordre de l'estil de: 'git config submodule.%s.gitdir .git/"
+"modules/%s'"
+
+#: builtin/submodule--helper.c
+#, c-format
 msgid "Failed to register url for submodule path '%s'"
 msgstr "S'ha produït un error en registrar l'url per al camí de submòdul «%s»"
 
@@ -16494,6 +17887,30 @@
 msgstr "no s'ha pogut obtenir una revisió per a HEAD"
 
 #: builtin/submodule--helper.c
+msgid "git submodule--helper gitdir <name>"
+msgstr "git submodule--helper gitdir <nom>"
+
+#: builtin/submodule--helper.c
+msgid ""
+"could not set core.repositoryformatversion to 1.\n"
+"Please set it for migration to work, for example:\n"
+"git config core.repositoryformatversion 1"
+msgstr ""
+"no s'ha pogut establir  core.repositoryformatversion a 1.\n"
+"Si us plau, establiu-lo perquè funcioni la migració, per exemple:\n"
+"git config core.repositoryformatversion 1"
+
+#: builtin/submodule--helper.c
+msgid ""
+"could not enable submodulePathConfig extension. It is required\n"
+"for migration to work. Please enable it in the root repo:\n"
+"git config extensions.submodulePathConfig true"
+msgstr ""
+"no s'ha pogut habilitar l'extensió «submodulePathConfig». És necessària\n"
+"perquè la migració funcioni. Habiliteu-la al dipòsit arrel:\n"
+"git config extensions.submodulePathConfig true"
+
+#: builtin/submodule--helper.c
 #, c-format
 msgid "Synchronizing submodule url for '%s'\n"
 msgstr "S'està sincronitzant l'url del submòdul per a «%s»\n"
@@ -16607,11 +18024,6 @@
 msgid "Value '%s' for submodule.alternateLocation is not recognized"
 msgstr "No es reconeix el valor «%s» per a submodule.alternateLocation"
 
-#: builtin/submodule--helper.c submodule.c
-#, c-format
-msgid "refusing to create/use '%s' in another submodule's git dir"
-msgstr "s'ha rebutjat crear/usar «%s» en el directori git d'un altre submòdul"
-
 #: builtin/submodule--helper.c
 #, c-format
 msgid "directory not empty: '%s'"
@@ -16622,6 +18034,15 @@
 msgid "clone of '%s' into submodule path '%s' failed"
 msgstr "el clonatge de «%s» al camí de submòdul «%s» ha fallat"
 
+#: builtin/submodule--helper.c submodule.c
+#, c-format
+msgid ""
+"refusing to create/use '%s' in another submodule's git dir. Enabling "
+"extensions.submodulePathConfig should fix this."
+msgstr ""
+"s'ha rebutjat crear/usar «%s» en el directori git d'un altre submòdul. "
+"Habilitar extensions.submodulePathConfig hauria de resoldre això."
+
 #: builtin/submodule--helper.c
 #, c-format
 msgid "could not get submodule directory for '%s'"
@@ -16662,8 +18083,8 @@
 "<filter-spec>] --url <url> --path <path>"
 msgstr ""
 "git submodule--helper clone [--prefix=<camí>] [--quiet] [--reference "
-"<repositori>] [--name <nom>] [--depth <depth>] [--single-branch] [--filter "
-"<especificacio-filtre>] --url <url> --path <camí>"
+"<repositori>] [--name <nom>] [--depth <profunditat>] [--single-branch] [--"
+"filter <especificacio-filtre>] --url <url> --path <camí>"
 
 #: builtin/submodule--helper.c
 #, c-format
@@ -16799,7 +18220,7 @@
 #, c-format
 msgid "Failed to recurse into submodule path '%s'"
 msgstr ""
-"s'ha produït un error en cercar recursivament al camí del submòdul «%s»"
+"S'ha produït un error en cercar recursivament al camí del submòdul «%s»"
 
 #: builtin/submodule--helper.c
 msgid "force checkout updates"
@@ -16993,7 +18414,7 @@
 msgid "'%s' already exists in the index and is not a submodule"
 msgstr "«%s» ja existeix en l'índex i no és submòdul"
 
-#: builtin/submodule--helper.c read-cache.c
+#: builtin/submodule--helper.c object-file.c
 #, c-format
 msgid "'%s' does not have a commit checked out"
 msgstr "«%s» no té una comissió comprovada"
@@ -17090,7 +18511,7 @@
 "        <tagname> [<commit> | <object>]"
 msgstr ""
 "git tag [-a | -s | -u <id-clau>] [-f] [-m <msg> | -F <fitxer>] [-e]\n"
-"        [(--trailer <token>[(=|:)<valor>])...]\n"
+"        [(--trailer <testimoni>[(=|:)<valor>])...]\n"
 "        <nom-etiqueta> [<comissió> | <objecte>]"
 
 #: builtin/tag.c
@@ -17760,7 +19181,7 @@
 msgstr "informa dels arbres de treball podats"
 
 #: builtin/worktree.c
-msgid "expire working trees older than <time>"
+msgid "prune missing working trees older than <time>"
 msgstr "fes caducar els arbres de treball més antics que <data>"
 
 #: builtin/worktree.c
@@ -17847,15 +19268,8 @@
 msgstr "S'està preparant l'arbre de treball (HEAD %s separat)"
 
 #: builtin/worktree.c
-#, c-format
-msgid ""
-"HEAD points to an invalid (or orphaned) reference.\n"
-"HEAD path: '%s'\n"
-"HEAD contents: '%s'"
-msgstr ""
-"HEAD apunta a una referència no vàlida (o òrfena).\n"
-"Camí HEAD: «%s»\n"
-"Contingut HEAD: «%s»"
+msgid "HEAD points to an invalid (or orphaned) reference.\n"
+msgstr "HEAD apunta a una referència no vàlida (o òrfena).\n"
 
 #: builtin/worktree.c
 msgid ""
@@ -17930,7 +19344,7 @@
 msgstr "mostra les anotacions esteses i les raons, si estan disponibles"
 
 #: builtin/worktree.c
-msgid "add 'prunable' annotation to worktrees older than <time>"
+msgid "add 'prunable' annotation to missing worktrees older than <time>"
 msgstr ""
 "afegeix l'anotació «prunable» als arbres de treball més antics que <data>"
 
@@ -18078,10 +19492,6 @@
 msgid "only useful for debugging"
 msgstr "només útil per a la depuració"
 
-#: bulk-checkin.c
-msgid "core.fsyncMethod = batch is unsupported on this platform"
-msgstr "core.fsyncMethod = batch no és compatible amb aquesta plataforma"
-
 #: bundle-uri.c
 #, c-format
 msgid "could not parse bundle list key %s with value '%s'"
@@ -18094,6 +19504,11 @@
 msgstr "la llista de farcells a «%s» no té mode"
 
 #: bundle-uri.c
+#, c-format
+msgid "bundle list at '%s': bundle '%s' has no uri"
+msgstr "la llista de farcells a «%s»: el farcell «%s» no té uri"
+
+#: bundle-uri.c
 msgid "failed to create temporary file"
 msgstr "no s'ha pogut crear un fitxer temporal"
 
@@ -18122,6 +19537,11 @@
 
 #: bundle-uri.c
 #, c-format
+msgid "bundle '%s' has no uri"
+msgstr "el farcell «%s» no té uri"
+
+#: bundle-uri.c
+#, c-format
 msgid "failed to download bundle from URI '%s'"
 msgstr "no s'ha pogut baixar el paquet de l'URI «%s»"
 
@@ -18563,6 +19983,10 @@
 msgstr "Mostra informació d'ajuda del Git"
 
 #: command-list.h
+msgid "EXPERIMENTAL: Rewrite history"
+msgstr "EXPERIMENTAL: rescriu la història"
+
+#: command-list.h
 msgid "Run git hooks"
 msgstr "Executa els lligams del git"
 
@@ -18602,6 +20026,11 @@
 "Afegeix o analitza la informació estructurada en els missatges de comissió"
 
 #: command-list.h
+msgid "EXPERIMENTAL: Show when files were last modified"
+msgstr ""
+"EXPERIMENTAL: mostra quan s'han modificat els fitxers per darrera vegada"
+
+#: command-list.h
 msgid "Show commit logs"
 msgstr "Mostra els registres de comissió"
 
@@ -18700,8 +20129,8 @@
 "Empaqueta els caps i les etiquetes per a un accés eficient al repositori"
 
 #: command-list.h
-msgid "Compute unique ID for a patch"
-msgstr "Calcula un identificador únic per a cada pedaç"
+msgid "Compute unique IDs for patches"
+msgstr "Calcula identificadors únics per als pedaços"
 
 #: command-list.h
 msgid "Prune all unreachable objects from the object database"
@@ -18768,6 +20197,10 @@
 "funciona amb repositoris nus"
 
 #: command-list.h
+msgid "Retrieve information about the repository"
+msgstr "Obté informació sobre el repositori"
+
+#: command-list.h
 msgid "Generates a summary of pending changes"
 msgstr "Genera un resum dels canvis pendents"
 
@@ -18776,8 +20209,8 @@
 msgstr "Reutilitza la resolució registrada dels conflictes de fusió"
 
 #: command-list.h
-msgid "Reset current HEAD to the specified state"
-msgstr "Restableix la HEAD actual a l'estat especificat"
+msgid "Set `HEAD` or the index to a known state"
+msgstr "Assigna `HEAD` o l'índex a un estat conegut"
 
 #: command-list.h
 msgid "Restore working tree files"
@@ -18877,9 +20310,8 @@
 msgstr "Llegeix, modifica i suprimeix referències simbòliques"
 
 #: command-list.h
-msgid "Create, list, delete or verify a tag object signed with GPG"
-msgstr ""
-"Crea, llista, suprimeix o verifica un objecte d'etiqueta signat amb GPG"
+msgid "Create, list, delete or verify tags"
+msgstr "Crea, llista, suprimeix o verifica etiquetes"
 
 #: command-list.h
 msgid "Creates a temporary file with a blob's contents"
@@ -18993,7 +20425,7 @@
 
 #: command-list.h
 msgid "Git pack format"
-msgstr "format de paquet del Git"
+msgstr "Format de paquet del Git"
 
 #: command-list.h
 msgid "Git cryptographic signature formats"
@@ -19350,11 +20782,11 @@
 #: commit-graph.c
 #, c-format
 msgid ""
-"attempting to write a commit-graph, but 'commitGraph."
-"changedPathsVersion' (%d) is not supported"
+"attempting to write a commit-graph, but 'commitGraph.changedPathsVersion' "
+"(%d) is not supported"
 msgstr ""
-"s'ha intentat escriure un graf de comissió, però no s'admet 'commitGraph."
-"changedPathsVersion' (%d)"
+"s'ha intentat escriure un graf de comissió, però no s'admet "
+"'commitGraph.changedPathsVersion' (%d)"
 
 #: commit-graph.c
 msgid "too many commits to write graph"
@@ -19395,7 +20827,8 @@
 #, c-format
 msgid "root tree OID for commit %s in commit-graph is %s != %s"
 msgstr ""
-"OID de l'arbre arrel per a comissions %s en el graf de comissions és %s != %s"
+"l'OID de l'arbre arrel per a comissions %s en el graf de comissions és %s != "
+"%s"
 
 #: commit-graph.c
 #, c-format
@@ -19446,6 +20879,11 @@
 msgid "could not parse commit %s"
 msgstr "no s'ha pogut analitzar la comissió %s"
 
+#: commit.c object.c
+#, c-format
+msgid "object %s is a %s, not a %s"
+msgstr "l'objecte %s és %s, no pas %s"
+
 #: commit.c
 #, c-format
 msgid "%s %s is not a commit!"
@@ -19948,11 +21386,6 @@
 
 #: config.c
 #, c-format
-msgid "bad boolean config value '%s' for '%s'"
-msgstr "valor de configuració booleà erroni «%s» per a «%s»"
-
-#: config.c
-#, c-format
 msgid "failed to expand user dir in: '%s'"
 msgstr "s'ha produït un error en expandir el directori d'usuari en: «%s»"
 
@@ -19981,6 +21414,48 @@
 msgstr "no s'ha pogut analitzar la configuració de la línia d'ordres"
 
 #: config.c
+#, c-format
+msgid ""
+"\n"
+"To use the default comment string (#) please run\n"
+"\n"
+"%s"
+msgstr ""
+"\n"
+"Per a usar la cadena de comentari per defecte (#) executeu\n"
+"\n"
+"%s"
+
+#: config.c
+msgid "<comment string>"
+msgstr "<cadena de comentari>"
+
+#: config.c
+#, c-format
+msgid ""
+"\n"
+"To set a custom comment string please run\n"
+"\n"
+"%s\n"
+"where '%s' is the string you wish to use.\n"
+msgstr ""
+"\n"
+"Per a fixar una cadena de comentari personalitzada, executeu\n"
+"\n"
+"%s\n"
+"on «%s» és la cadena que voleu usar.\n"
+
+#: config.c
+#, c-format
+msgid "Support for '%s=auto' has been removed in Git 3.0"
+msgstr "La compatibilitat amb «%s=auto» s'ha eliminat de Git 3.0"
+
+#: config.c
+#, c-format
+msgid "Support for '%s=auto' is deprecated and will be removed in Git 3.0"
+msgstr "La compatibilitat amb «%s=auto» és obsoleta i s'eliminarà de Git 3.0"
+
+#: config.c
 msgid "unknown error occurred while reading the configuration files"
 msgstr "un error desconegut ha ocorregut en llegir els fitxers de configuració"
 
@@ -20178,7 +21653,7 @@
 #: connect.c
 #, c-format
 msgid "Looking up %s ... "
-msgstr "S'està cercant %s..."
+msgstr "S'està cercant %s... "
 
 #: connect.c
 #, c-format
@@ -20581,6 +22056,14 @@
 msgstr "s'ha produït un error en escriure arxiu"
 
 #: diff-lib.c
+msgid "max-depth is not supported for worktree diffs"
+msgstr "No s'admet max-depth per a diferències d'arbres de treball"
+
+#: diff-lib.c
+msgid "max-depth is not supported for index diffs"
+msgstr "no s'admet max-depth per a diferències d'índexs"
+
+#: diff-lib.c
 msgid "--merge-base does not work with ranges"
 msgstr "--merge-base no funciona amb intervals"
 
@@ -20607,7 +22090,7 @@
 #: diff-no-index.c
 msgid "git diff --no-index [<options>] <path> <path> [<pathspec>...]"
 msgstr ""
-"git diff --no-index [<opcions>] <camí> <camí> [<especificacio-camí>...]"
+"git diff --no-index [<opcions>] <camí> <camí> [<especificació-camí>...]"
 
 #: diff-no-index.c
 msgid ""
@@ -20669,11 +22152,6 @@
 msgstr ""
 "Valor desconegut de la variable de configuració de «diff.submodule»: «%s»"
 
-#: diff.c merge-ort.c transport.c
-#, c-format
-msgid "unknown value for config '%s': %s"
-msgstr "valor desconegut per al config «%s»': %s"
-
 #: diff.c
 #, c-format
 msgid ""
@@ -20695,7 +22173,7 @@
 #: diff.c
 #, c-format
 msgid "pathspec magic not supported by --follow: %s"
-msgstr "el «pathspec» màgic no està suportat per --follow: %s"
+msgstr "la màgia d'especificació de camí no està suportat per --follow: %s"
 
 #: diff.c parse-options.c
 #, c-format
@@ -20744,6 +22222,10 @@
 msgstr "valor desconegut després de ws-error-highlight=%.*s"
 
 #: diff.c
+msgid "--find-object requires a git repository"
+msgstr "--find-object requereix un repositori git"
+
+#: diff.c
 #, c-format
 msgid "unable to resolve '%s'"
 msgstr "no s'ha pogut resoldre «%s»"
@@ -21244,6 +22726,10 @@
 msgstr "seleccioneu els fitxers per tipus de diff"
 
 #: diff.c
+msgid "<depth>"
+msgstr "<profunditat>"
+
+#: diff.c
 msgid "<file>"
 msgstr "<fitxer>"
 
@@ -21456,7 +22942,7 @@
 "Això és degut probablement a la corrupció del repositori.\n"
 "Si el que voleu és intentar reparar aquesta corrupció de repositori "
 "reobtenint l'objecte que manca, useu 'git fetch --refetch' amb l'objecte que "
-"manca"
+"manca."
 
 #: fetch-pack.c
 msgid "git fetch-pack: expected shallow list"
@@ -21496,11 +22982,6 @@
 
 #: fetch-pack.c
 #, c-format
-msgid "object not found: %s"
-msgstr "objecte no trobat: %s"
-
-#: fetch-pack.c
-#, c-format
 msgid "error in object: %s"
 msgstr "error en objecte: %s"
 
@@ -21691,6 +23172,11 @@
 msgstr "git fetch-pack: s'esperava un paquet de final de resposta"
 
 #: fetch-pack.c
+#, c-format
+msgid "couldn't resolve 'auto' filter '%s': %s"
+msgstr "no s'ha pogut resoldre el filtre «auto» «%s»: %s"
+
+#: fetch-pack.c
 msgid "no matching remote head"
 msgstr "no hi ha cap HEAD remot coincident"
 
@@ -21848,6 +23334,11 @@
 msgstr "àlies recursiu: %s"
 
 #: git.c
+#, c-format
+msgid "alias loop detected: expansion of '%s' does not terminate:%s"
+msgstr "bucle d'àlies detectat expansió de «%s» no acaba:%s"
+
+#: git.c
 msgid "write failure on standard output"
 msgstr "fallada d'escriptura en la sortida estàndard"
 
@@ -21861,11 +23352,6 @@
 
 #: git.c
 #, c-format
-msgid "alias loop detected: expansion of '%s' does not terminate:%s"
-msgstr "bucle d'àlies detectat expansió de «%s» no acaba:%s"
-
-#: git.c
-#, c-format
 msgid "cannot handle %s as a builtin"
 msgstr "no es pot gestionar %s com a integrat"
 
@@ -21929,6 +23415,11 @@
 msgstr "no s'ha pogut obtenir l'empremta ssh de la clau  «%s»"
 
 #: gpg-interface.c
+#, c-format
+msgid "failed to get the ssh fingerprint for key %s"
+msgstr "no s'ha pogut obtenir l'empremta ssh de la clau «%s»"
+
+#: gpg-interface.c
 msgid ""
 "either user.signingkey or gpg.ssh.defaultKeyCommand needs to be configured"
 msgstr ""
@@ -21936,6 +23427,11 @@
 
 #: gpg-interface.c
 #, c-format
+msgid "malformed build-time gpg.ssh.defaultKeyCommand: %s"
+msgstr "ordre gpg.ssh.defaultKeyCommand errònia a l'hora de construir: %s"
+
+#: gpg-interface.c
+#, c-format
 msgid "gpg.ssh.defaultKeyCommand succeeded but returned no keys: %s %s"
 msgstr ""
 "gpg.ssh.defaultKeyCommand ha tingut èxit però no ha retornat cap clau: %s %s"
@@ -22189,7 +23685,21 @@
 "You can disable this warning with `git config set advice.ignoredHook false`."
 msgstr ""
 "El lligam «%s» s'ha ignorat perquè no s'ha establert com a executable.\n"
-"Podeu desactivar aquest avís amb «git config advice.ignoredHook false». "
+"Podeu desactivar aquest avís amb git config advice.ignoredHook false`."
+
+#: hook.c
+#, c-format
+msgid "disabled hook '%s' has no command configured"
+msgstr "el lligam inhabilitat «%s» no té cap ordre configurada"
+
+#: hook.c
+#, c-format
+msgid ""
+"'hook.%s.command' must be configured or 'hook.%s.event' must be removed; "
+"aborting."
+msgstr ""
+"s'ha de configurar «hook.%s.command» o s'ha de suprimir «hook.%s.event»; "
+"s'està avortant."
 
 #: http-fetch.c
 msgid "not a git repository"
@@ -22254,6 +23764,35 @@
 "  petició: %s\n"
 "   redirecció: %s"
 
+#: http.c
+#, c-format
+msgid ""
+"response requested a delay greater than http.maxRetryTime (%ld > %ld seconds)"
+msgstr ""
+"la resposta ha demanat un retard superior a http.maxRetryTime (%ld > %ld "
+"segons)"
+
+#: http.c
+#, c-format
+msgid ""
+"configured http.retryAfter exceeds http.maxRetryTime (%ld > %ld seconds)"
+msgstr ""
+"el valor configurat de http.retryAfter supera http.maxRetryTime (%ld > %ld "
+"segons)"
+
+#: http.c
+#, c-format
+msgid "rate limited, waiting %ld seconds before retry"
+msgstr "velocitat limitada; esperant %ld segons abans de reintentar"
+
+#: http.h
+#, c-format
+msgid ""
+"number too large to represent as curl_off_t on this platform: %<PRIuMAX>"
+msgstr ""
+"el nombre és massa gran per a representar-lo amb curl_off_t en aquesta "
+"plataforma: %<PRIuMAX>"
+
 #: ident.c
 msgid "Author identity unknown\n"
 msgstr "Identitat de l'autor desconeguda\n"
@@ -22350,6 +23889,10 @@
 "(p. ex., 'git config imap.folder Drafts')"
 
 #: list-objects-filter-options.c
+msgid "'auto' filter not supported by this command"
+msgstr "aquesta ordre no admet el filtre «auto»"
+
+#: list-objects-filter-options.c
 msgid "expected 'tree:<depth>'"
 msgstr "s'esperava «tree:<profunditat>»"
 
@@ -22373,6 +23916,10 @@
 msgstr "cal escapar el caràcter en el sub-filter-spec «%c»"
 
 #: list-objects-filter-options.c
+msgid "an 'auto' filter cannot be combined"
+msgstr " no es pot combinar un filtre «auto»"
+
+#: list-objects-filter-options.c
 msgid "expected something after combine:"
 msgstr "s'esperava alguna cosa després de combinar:"
 
@@ -22381,6 +23928,10 @@
 msgstr "no es poden combinar múltiples especificacions de filtratge"
 
 #: list-objects-filter-options.c
+msgid "an 'auto' filter is incompatible with any other filter"
+msgstr "un filtre «auto» és incompatible amb qualsevol altre filtre"
+
+#: list-objects-filter-options.c
 msgid "unable to upgrade repository format to support partial clone"
 msgstr ""
 "no s'ha pogut actualitzar el format del repositori perquè sigui compatible "
@@ -22421,23 +23972,49 @@
 
 #: lockfile.c
 #, c-format
+msgid "could not write lock pid file '%s'"
+msgstr "no s'ha pogut escriure el fitxer de blocatge del pid «%s»"
+
+#: lockfile.c
+#, c-format
+msgid "malformed lock pid file '%s'"
+msgstr "fitxer de blocatge del pid «%s» malmès"
+
+#: lockfile.c
+#, c-format
 msgid ""
-"Unable to create '%s.lock': %s.\n"
+"Unable to create '%s': %s.\n"
 "\n"
-"Another git process seems to be running in this repository, e.g.\n"
-"an editor opened by 'git commit'. Please make sure all processes\n"
-"are terminated then try again. If it still fails, a git process\n"
-"may have crashed in this repository earlier:\n"
-"remove the file manually to continue."
 msgstr ""
-"No s'ha pogut crear «%s.lock»: %s.\n"
+"No s'ha pogut crear «%s»: %s.\n"
 "\n"
-"Sembla que un altre procés git s'està executant en aquest\n"
-"repositori, per exemple, un editor obert per «git commit». \n"
-"Assegureu-vos que tots els processos s'hagin acabat i\n"
-"llavors proveu de nou. Si encara falla, pot ser un procés git\n"
-"ha fallat en aquest repositori abans:\n"
-"elimineu el fitxer manualment per a continuar."
+
+#: lockfile.c
+#, c-format
+msgid ""
+"Lock may be held by process %<PRIuMAX>; if no git process is running, the "
+"lock file may be stale (PIDs can be reused)"
+msgstr ""
+"El blocatge pot estar retingut pel procés %<PRIuMAX>; si no hi ha cap procés "
+"en execució, el fitxer de blocatge pot estar estancat (els PID poden ser "
+"reutilitzats)"
+
+#: lockfile.c
+#, c-format
+msgid ""
+"Lock was held by process %<PRIuMAX>, which is no longer running; the lock "
+"file appears to be stale"
+msgstr ""
+"El blocatge l'havia fet el procés %<PRIuMAX>, el qual ja no està en "
+"execució; el fitxer de blocatge sembla estar estancat"
+
+#: lockfile.c
+msgid ""
+"Another git process seems to be running in this repository, or the lock file "
+"may be stale"
+msgstr ""
+"Un altre procés git s'està executant en aquest repositori, o potser el "
+"fitxer de blocatge està estancat"
 
 #: lockfile.c
 #, c-format
@@ -22805,13 +24382,17 @@
 msgstr "línia mal formada: %s"
 
 #: midx-write.c
-msgid "could not load pack"
-msgstr "no s'ha pogut carregar el paquet"
+#, c-format
+msgid "could not load pack %d"
+msgstr "no s'ha pogut carregar el paquet %d"
 
 #: midx-write.c
-#, c-format
-msgid "could not open index for %s"
-msgstr "s'ha produït un error en obrir l'índex per «%s»"
+msgid "too many packs, unable to compact"
+msgstr "massa paquets; no s'ha pogut compactar"
+
+#: midx-write.c pack-revindex.c
+msgid "could not determine preferred pack"
+msgstr "no s'ha pogut determinar el paquet preferit"
 
 #: midx-write.c
 #, c-format
@@ -22824,6 +24405,15 @@
 msgstr "no s'ha pogut netejar l'índex multipaquet a %s"
 
 #: midx-write.c
+#, c-format
+msgid "unknown MIDX version: %d"
+msgstr "versió desconeguda de MIDX: %d"
+
+#: midx-write.c
+msgid "cannot perform MIDX compaction with v1 format"
+msgstr "no puc fer la compactació MIDX amb el format v1"
+
+#: midx-write.c
 msgid "ignoring existing multi-pack-index; checksum mismatch"
 msgstr ""
 "s'està ignorant l'índex multipaquet existent; la suma de verificació no "
@@ -22845,6 +24435,11 @@
 
 #: midx-write.c
 #, c-format
+msgid "failed to open preferred pack %s"
+msgstr "no s'ha pogut obrir el paquet preferit %s"
+
+#: midx-write.c
+#, c-format
 msgid "cannot select preferred pack %s with no objects"
 msgstr "no es pot seleccionar un paquet preferit %s sense objectes"
 
@@ -22875,6 +24470,10 @@
 msgstr "no s'han pogut escriure els mapes de bits dels multipaquets"
 
 #: midx-write.c
+msgid "too many multi-pack-indexes"
+msgstr "massa índexs multipaquet"
+
+#: midx-write.c
 msgid "unable to open multi-pack-index chain file"
 msgstr "no s'ha pogut obrir el fitxer de cadenes multi-path-index"
 
@@ -22940,8 +24539,8 @@
 #, c-format
 msgid "multi-pack-index signature 0x%08x does not match signature 0x%08x"
 msgstr ""
-"la signatura de l'índex multipaquet 0x%08x no coincideix amb la signatura 0x"
-"%08x"
+"la signatura de l'índex multipaquet 0x%08x no coincideix amb la signatura "
+"0x%08x"
 
 #: midx.c
 #, c-format
@@ -23227,11 +24826,6 @@
 
 #: object-file.c
 #, c-format
-msgid "unable to write file %s"
-msgstr "no s'ha pogut escriure al fitxer %s"
-
-#: object-file.c
-#, c-format
 msgid "unable to write repeatedly vanishing file %s"
 msgstr "no puc escriure el fitxer %s que desapareix repetidament"
 
@@ -23241,6 +24835,10 @@
 msgstr "no s'ha pogut establir el permís a «%s»"
 
 #: object-file.c
+msgid "core.fsyncMethod = batch is unsupported on this platform"
+msgstr "core.fsyncMethod = batch no és compatible amb aquesta plataforma"
+
+#: object-file.c
 msgid "error when closing loose object file"
 msgstr "error en tancar el fitxer d'objecte solt"
 
@@ -23329,6 +24927,10 @@
 msgstr "%s: no s'han pogut inserir a la base de dades"
 
 #: object-file.c
+msgid "cannot add a submodule of a different hash algorithm"
+msgstr "no es pot afegir un submòdul d'un algorisme de hash diferent"
+
+#: object-file.c
 #, c-format
 msgid "%s: unsupported file type"
 msgstr "%s: tipus de fitxer no suportat"
@@ -23546,11 +25148,6 @@
 
 #: object.c
 #, c-format
-msgid "object %s is a %s, not a %s"
-msgstr "l'objecte %s és %s, no pas %s"
-
-#: object.c
-#, c-format
 msgid "object %s has unknown type id %d"
 msgstr "l'objecte %s té un identificador de tipus %d desconegut"
 
@@ -23561,6 +25158,11 @@
 
 #: object.c
 #, c-format
+msgid "unable to open object stream for %s"
+msgstr "no s'ha pogut obrir el flux d'objectes per a %s"
+
+#: object.c
+#, c-format
 msgid "hash mismatch %s"
 msgstr "el resum no coincideix %s"
 
@@ -23583,18 +25185,6 @@
 "massa profunda"
 
 #: odb.c
-msgid "unable to fdopen alternates lockfile"
-msgstr "no s'ha pogut fer «fdopen» al fitxer de blocatge alternatiu"
-
-#: odb.c
-msgid "unable to read alternates file"
-msgstr "no es pot llegir el fitxer «alternates»"
-
-#: odb.c
-msgid "unable to move new alternates file into place"
-msgstr "no s'ha pogut moure el nou fitxer «alternates» al lloc"
-
-#: odb.c
 #, c-format
 msgid "path '%s' does not exist"
 msgstr "el camí «%s» no existeix"
@@ -23651,6 +25241,18 @@
 msgid "%s is not a valid '%s' object"
 msgstr "%s no és un objecte de «%s» vàlid"
 
+#: odb/source-files.c
+msgid "unable to fdopen alternates lockfile"
+msgstr "no s'ha pogut fer «fdopen» al fitxer de blocatge alternatiu"
+
+#: odb/source-files.c
+msgid "unable to read alternates file"
+msgstr "no es pot llegir el fitxer «alternates»"
+
+#: odb/source-files.c
+msgid "unable to move new alternates file into place"
+msgstr "no s'ha pogut moure el nou fitxer «alternates» al lloc"
+
 #: pack-bitmap-write.c
 #, c-format
 msgid "duplicate entry when writing bitmap index: %s"
@@ -23874,6 +25476,26 @@
 msgid "mtimes file %s is corrupt"
 msgstr "el fitxer mtimes %s està malmès"
 
+#: pack-refs.c
+msgid "pack everything"
+msgstr "empaqueta-ho tot"
+
+#: pack-refs.c
+msgid "prune loose refs (default)"
+msgstr "poda les referències soltes (per defecte)"
+
+#: pack-refs.c
+msgid "auto-pack refs as needed"
+msgstr "auto-empaqueta referències si cal"
+
+#: pack-refs.c
+msgid "references to include"
+msgstr "referències a incloure"
+
+#: pack-refs.c
+msgid "references to exclude"
+msgstr "referències a excloure"
+
 #: pack-revindex.c
 #, c-format
 msgid "reverse-index file %s is too small"
@@ -23914,10 +25536,6 @@
 msgstr ""
 "el fragment de l'index invers de l'índex multipaquet és de mida incorrecta"
 
-#: pack-revindex.c
-msgid "could not determine preferred pack"
-msgstr "no s'ha pogut determinar el paquet preferit"
-
 #: pack-write.c
 msgid "cannot both write and verify reverse index"
 msgstr "no es pot escriure i verificar l'índex invers"
@@ -23948,6 +25566,11 @@
 
 #: packfile.c
 #, c-format
+msgid "could not load .mtimes for cruft pack '%s'"
+msgstr "no s'ha pogut carregar .mtimes per al paquet superflu «%s»"
+
+#: packfile.c
+#, c-format
 msgid "offset before start of pack index for %s (corrupt index?)"
 msgstr ""
 "desplaçament abans d'inici d'índex de paquet per a %s (índex corromput?)"
@@ -23996,7 +25619,7 @@
 #: parse-options.c
 #, c-format
 msgid "value for %s exceeds %<PRIdMAX>"
-msgstr "el valor de %s supera %<PRIdMAX> "
+msgstr "el valor de %s supera %<PRIdMAX>"
 
 #: parse-options.c
 #, c-format
@@ -24378,6 +26001,11 @@
 msgid "unable to parse --pretty format"
 msgstr "no s'ha pogut analitzar el format --pretty"
 
+#: pretty.c
+#, c-format
+msgid "%s is not supported by this command"
+msgstr "aquesta ordre no admet %s"
+
 # lazy → tardà as in “lazy evaluation”
 # 2 línies OK?
 #: promisor-remote.c
@@ -24410,6 +26038,11 @@
 
 #: promisor-remote.c
 #, c-format
+msgid "unsupported field '%s' in '%s' config"
+msgstr "no s'admet el camp «%s» en la configuració «%s»"
+
+#: promisor-remote.c
+#, c-format
 msgid "no or empty URL advertised for remote '%s'"
 msgstr "no hi ha URL configurat per al remot «%s» o és buit"
 
@@ -24420,18 +26053,47 @@
 
 #: promisor-remote.c
 #, c-format
+msgid "invalid element '%s' from remote info"
+msgstr "element invàlid «%s» en la informació del remot"
+
+#: promisor-remote.c
+#, c-format
+msgid "server advertised a promisor remote without a name or URL: %s"
+msgstr "el servidor ha anunciat un remot «promisor» sense nom o URL: %s"
+
+#: promisor-remote.c
+#, c-format
+msgid ""
+"Storing new %s from server for remote '%s'.\n"
+"    '%s' -> '%s'\n"
+msgstr ""
+"Emmagatzemant nou %s des del servidor per al remot '%s'.\n"
+"    '%s' -> '%s'\n"
+
+#: promisor-remote.c
+#, c-format
+msgid "invalid filter '%s' for remote '%s' will not be stored: %s"
+msgstr "el filtre invàlid «%s» per al remot «%s» no s'emmagatzemarà: %s"
+
+#: promisor-remote.c
+#, c-format
+msgid "invalid token '%s' for remote '%s' will not be stored"
+msgstr "el testimoni invàlid «%s» per al remot «%s» no s'emmagatzemarà"
+
+#: promisor-remote.c
+#, c-format
 msgid "unknown '%s' value for '%s' config option"
 msgstr "valor desconegut «%s» per a l'opció «%s» de configuració"
 
 #: promisor-remote.c
 #, c-format
-msgid "unknown element '%s' from remote info"
-msgstr "valor desconegut «%s» en la informació del remot"
+msgid "accepted promisor remote '%s' not found"
+msgstr "no s'ha trobat el remot promisor acceptat «%s»"
 
 #: promisor-remote.c
 #, c-format
-msgid "accepted promisor remote '%s' not found"
-msgstr "no s'ha trobat el remot promisor acceptat «%s»"
+msgid "promisor remote '%s' advertised invalid filter '%s': %s"
+msgstr "el remot de «promissor» «%s» ha anunciat un filtre invàlid «%s»:%s"
 
 #: protocol-caps.c
 msgid "object-info: expected flush after arguments"
@@ -24563,6 +26225,17 @@
 
 #: range-diff.c
 #, c-format
+msgid ""
+"range-diff: unable to compute the range-diff, since it exceeds the maximum "
+"memory for the cost matrix: %s (%<PRIuMAX> bytes) needed, limited to %s "
+"(%<PRIuMAX> bytes)"
+msgstr ""
+"range-diff: no s'ha pogut calcular el range-diff, ja que excedeix la memòria "
+"màxima per a la matriu de cost: calen %s(%<PRIuMAX> octets) i el límit és de "
+"%s (%<PRIuMAX> octets)"
+
+#: range-diff.c
+#, c-format
 msgid "could not parse log for '%s'"
 msgstr "no s'ha pogut llegir el fitxer de registre per a «%s»"
 
@@ -24755,6 +26428,15 @@
 
 #: read-cache.c
 #, c-format
+msgid ""
+"Skipping submodule due to ignore=all: %s\n"
+"Use --force if you really want to add the submodule."
+msgstr ""
+"Ometent submòdul per causa d'ingore=all: %s\n"
+"Utilitzeu --force si voleu de debò afegir el submòdul."
+
+#: read-cache.c
+#, c-format
 msgid "unexpected diff status %c"
 msgstr "estat de diff inesperat %c"
 
@@ -25110,6 +26792,11 @@
 msgstr "no es pot usar --format=%.*s amb --python, --shell, --tcl"
 
 #: ref-filter.c
+#, c-format
+msgid "parse_object_buffer failed on %s for %s"
+msgstr "parse_object_buffer ha fallat en %s per a %s"
+
+#: ref-filter.c
 msgid "failed to run 'describe'"
 msgstr "no s'ha pogut executar «describe»"
 
@@ -25149,11 +26836,6 @@
 
 #: ref-filter.c
 #, c-format
-msgid "parse_object_buffer failed on %s for %s"
-msgstr "parse_object_buffer ha fallat en %s per a %s"
-
-#: ref-filter.c
-#, c-format
 msgid "malformed object at '%s'"
 msgstr "objecte mal format a «%s»"
 
@@ -25206,25 +26888,36 @@
 
 #: refs.c
 #, c-format
+msgid "in '%s' phase, update aborted by the reference-transaction hook"
+msgstr ""
+"en la fase «%s», actualització avortada pel lligam reference-transaction"
+
+#: refs.c
+msgid "Checking references consistency"
+msgstr "S'està comprovant la consistència de les referències"
+
+#: refs.c
+#, c-format
 msgid "%s does not point to a valid object!"
-msgstr "%s no apunta a un objecte vàlid"
+msgstr "%s no apunta a un objecte vàlid!"
 
 #: refs.c
 #, c-format
 msgid "%s%s will become dangling after %s is deleted\n"
-msgstr " %s%s es quedarà despenjat després d'esborrar %s\n"
+msgstr "%s%s es quedarà despenjat després d'esborrar %s\n"
 
 #: refs.c
 #, c-format
 msgid "%s%s has become dangling after %s was deleted\n"
-msgstr " %s%s s'ha quedat despenjat després d'esborrar %s\n"
+msgstr "%s%s s'ha quedat despenjat després d'esborrar %s\n"
 
 #: refs.c
 #, c-format
 msgid ""
 "Using '%s' as the name for the initial branch. This default branch name\n"
-"is subject to change. To configure the initial branch name to use in all\n"
-"of your new repositories, which will suppress this warning, call:\n"
+"will change to \"main\" in Git 3.0. To configure the initial branch name\n"
+"to use in all of your new repositories, which will suppress this warning,\n"
+"call:\n"
 "\n"
 "\tgit config --global init.defaultBranch <name>\n"
 "\n"
@@ -25234,8 +26927,8 @@
 "\tgit branch -m <name>\n"
 msgstr ""
 "S'està utilitzant «%s» com a nom de la branca inicial. Aquest nom de branca\n"
-"per defecte es pot canviar. Per a configurar el nom inicial de la branca "
-"que\n"
+"per defecte canviarà a \"main\" en Git 3.0. Per a configurar el nom inicial "
+"de la branca que\n"
 "s'utilitzarà en tots els repositoris nous, i que suprimirà aquest avís, "
 "useu:\n"
 "\n"
@@ -25249,6 +26942,21 @@
 
 #: refs.c
 #, c-format
+msgid ""
+"Using '%s' as the name for the initial branch since Git 3.0.\n"
+"If you expected Git to create 'master', the just-created\n"
+"branch can be renamed via this command:\n"
+"\n"
+"\tgit branch -m master\n"
+msgstr ""
+"S'usa '%s' com a nom de la branca inicial des de Git 3.0.\n"
+"Si esperàveu que Git crearia 'master', es pot canviar el nom\n"
+"de la la branca que s'acaba de crear amb l'ordre:\n"
+"\n"
+"\tgit branch -m master\n"
+
+#: refs.c
+#, c-format
 msgid "could not retrieve `%s`"
 msgstr "no s'ha pogut recuperar «%s»"
 
@@ -25318,10 +27026,6 @@
 msgstr "no està permès actualitzar les referències en un entorn de quarantena"
 
 #: refs.c
-msgid "ref updates aborted by hook"
-msgstr "les actualitzacions de referències s'han avortat per un lligam"
-
-#: refs.c
 #, c-format
 msgid "'%s' exists; cannot create '%s'"
 msgstr "«%s» existeix; no es pot crear «%s»"
@@ -25361,6 +27065,31 @@
 msgid "migrated refs can be found at '%s'"
 msgstr "les referències migrades es poden trobar en «%s»"
 
+#: refs/files-backend.c
+msgid ""
+"'core.preferSymlinkRefs=true' is nominated for removal.\n"
+"hint: The use of symbolic links for symbolic refs is deprecated\n"
+"hint: and will be removed in Git 3.0. The configuration that\n"
+"hint: tells Git to use them is thus going away. You can unset\n"
+"hint: it with:\n"
+"hint:\n"
+"hint:\tgit config unset core.preferSymlinkRefs\n"
+"hint:\n"
+"hint: Git will then use the textual symref format instead."
+msgstr ""
+"'core.preferSymlinkRefs=true' està programat per a ser eliminat.\n"
+"consell: L'ús d'enllaços simbòlics per a les referències simbòliques és "
+"obsolet\n"
+"consell: i s'eliminarà de  Git 3.0. La configuració que diu\n"
+"consell: a Git que les ha d'usar, per tant, desapareixerà. Podeu desassignar-"
+"la\n"
+"consell: amb:\n"
+"consell:\n"
+"consell:\tgit config unset core.preferSymlinkRefs\n"
+"consell:\n"
+"consell: Així Git usarà en el seu lloc el format de referència simbòlica "
+"textual."
+
 #: refs/files-backend.c refs/reftable-backend.c
 #, c-format
 msgid ""
@@ -25369,6 +27098,13 @@
 "no puc bloquejar la referència«%s»: s'esperava una referència\n"
 "simbòlica amb destinació «%s» però és una referència regular"
 
+#: refs/files-backend.c refs/reftable-backend.c
+#, c-format
+msgid "trying to write reflog for '%s' with incomplete values"
+msgstr ""
+"s'està intentant escriure el registre de referències «%s» amb valors "
+"incomplets"
+
 #: refs/files-backend.c
 #, c-format
 msgid "cannot read ref file '%s'"
@@ -25379,10 +27115,6 @@
 msgid "cannot open directory %s"
 msgstr "no es pot obrir el directori «%s»"
 
-#: refs/files-backend.c
-msgid "Checking references consistency"
-msgstr "S'està comprovant la consistència de les referències"
-
 #: refs/packed-backend.c
 #, c-format
 msgid "unable to open '%s'"
@@ -25439,6 +27171,14 @@
 # bloquejar→blocar?
 #: refs/reftable-backend.c
 #, c-format
+msgid "cannot lock ref '%s': dangling symref already exists"
+msgstr ""
+"no puc bloquejar la referència «%s»: encara hi ha una referència simbòlica "
+"despenjada"
+
+# bloquejar→blocar?
+#: refs/reftable-backend.c
+#, c-format
 msgid "cannot lock ref '%s': reference already exists"
 msgstr "no puc bloquejar la referència «%s»: la referència ja existeix"
 
@@ -25482,6 +27222,22 @@
 msgstr ""
 "el nom de referència %s és una referència simbòlica: no es permet copiar"
 
+#: refs/reftable-backend.c
+#, c-format
+msgid "reftable stack for worktree '%s' is broken"
+msgstr ""
+"la pila de taula de referències per a l'arbre de treball '%s\" està trencada"
+
+#: refs/reftable-backend.c
+#, c-format
+msgid "could not create iterator for worktree '%s'"
+msgstr "no s'ha pogut crear l'iterador per a l'arbre de treball «%s»"
+
+#: refs/reftable-backend.c
+#, c-format
+msgid "could not read record for worktree '%s'"
+msgstr "no s'ha pogut llegir el registre per a l'arbre de treball «%s»"
+
 #: refspec.c
 #, c-format
 msgid "pattern '%s' has no '*'"
@@ -25536,6 +27292,19 @@
 
 #: remote-curl.c
 #, c-format
+msgid "rate limited by '%s', please try again in %ld seconds"
+msgstr ""
+"velocitat limitada per «%s»; si us plau, intenteu-ho una altra volta dins de "
+"%ld segons"
+
+#: remote-curl.c
+#, c-format
+msgid "rate limited by '%s', please try again later"
+msgstr ""
+"velocitat limitada per «%s»; si us plau, intenteu-ho una altra volta més tard"
+
+#: remote-curl.c
+#, c-format
 msgid "unable to access '%s': %s"
 msgstr "no s'ha pogut accedir a «%s»: %s"
 
@@ -25573,10 +27342,6 @@
 msgstr "RPC ha fallat; %s"
 
 #: remote-curl.c
-msgid "cannot handle pushes this big"
-msgstr "no es pot gestionar pujades tan grans"
-
-#: remote-curl.c
 #, c-format
 msgid "cannot deflate request; zlib deflate error %d"
 msgstr "no es pot descomprimir la sol·licitud; error de deflate zlib %d"
@@ -25803,6 +27568,14 @@
 
 #: remote.c
 #, c-format
+msgid ""
+"The <src> part of the refspec ('%s') is an object ID that doesn't exist.\n"
+msgstr ""
+"La part <src> de l'especificador de referència («%s») és l'identificador "
+"d'un objecte que no existeix.\n"
+
+#: remote.c
+#, c-format
 msgid "%s cannot be resolved to branch"
 msgstr "«%s» no es pot resoldre a una branca"
 
@@ -25878,12 +27651,12 @@
 
 #: remote.c
 #, c-format
-msgid "Your branch is based on '%s', but the upstream is gone.\n"
-msgstr "La vostra branca està basada en «%s», però la font no hi és.\n"
-
-#: remote.c
-msgid "  (use \"git branch --unset-upstream\" to fixup)\n"
-msgstr "  (useu «git branch --unset-upstream» per a arreglar-ho)\n"
+msgid ""
+"ignoring value '%s' for status.compareBranches, only @{upstream} and @{push} "
+"are supported"
+msgstr ""
+"s'ignora el valor «%s» per a status.compareBranches; només s'admeten "
+"@{upstream} i @{push}"
 
 #: remote.c
 #, c-format
@@ -25950,6 +27723,15 @@
 
 #: remote.c
 #, c-format
+msgid "Your branch is based on '%s', but the upstream is gone.\n"
+msgstr "La vostra branca està basada en «%s», però la font no hi és.\n"
+
+#: remote.c
+msgid "  (use \"git branch --unset-upstream\" to fixup)\n"
+msgstr "  (useu «git branch --unset-upstream» per a arreglar-ho)\n"
+
+#: remote.c
+#, c-format
 msgid "cannot parse expected object name '%s'"
 msgstr "no es pot analitzar el nom de l'objecte esperat «%s»"
 
@@ -25958,6 +27740,86 @@
 msgid "cannot strip one component off url '%s'"
 msgstr "no es pot despullar un component de l'url «%s»"
 
+#: repack-geometry.c
+#, c-format
+msgid "cannot open index for %s"
+msgstr "no s'ha pogut obrir l'índex per a %s"
+
+#: repack-geometry.c
+#, c-format
+msgid "pack %s too large to consider in geometric progression"
+msgstr ""
+"el paquet %s és massa gran per a considerar-ho en progressió geomètrica"
+
+#: repack-geometry.c
+#, c-format
+msgid "pack %s too large to roll up"
+msgstr "el paquet %s és massa gran per a enrotllar-lo"
+
+#: repack-midx.c
+#, c-format
+msgid "could not open tempfile %s for writing"
+msgstr "no s'ha pogut obrir el fitxer temporal «%s» per a escriptura"
+
+#: repack-midx.c
+msgid "could not close refs snapshot tempfile"
+msgstr ""
+"no s'ha pogut tancar el fitxer temporal amb la instantània de referències"
+
+#: repack-midx.c
+#, c-format
+msgid "could not remove stale bitmap: %s"
+msgstr "no s'ha pogut eliminar el mapa de bits estancat: %s"
+
+#: repack-promisor.c
+msgid "could not start pack-objects to repack promisor objects"
+msgstr ""
+"no s'ha pogut iniciar pack-objects per a tornar a empaquetar els objectes "
+"«promisor»"
+
+#: repack-promisor.c
+msgid "failed to feed promisor objects to pack-objects"
+msgstr "no s'ha pogut alimentar pack-objects amb objectes «promisor»"
+
+#: repack-promisor.c repack.c
+msgid "repack: Expecting full hex object ID lines only from pack-objects."
+msgstr ""
+"repack: s'esperen només línies amb l'id d'objecte hexadecimal complet des de "
+"pack-objects."
+
+#: repack-promisor.c
+msgid "could not finish pack-objects to repack promisor objects"
+msgstr ""
+"no s'ha pogut finalitzar pack-objects per a tornar a empaquetar els objectes "
+"«promisor»"
+
+#: repack-promisor.c
+msgid "could not start pack-objects to repack promisor packs"
+msgstr ""
+"no s'ha pogut iniciar pack-objects per a tornar a empaquetar els paquets "
+"«promisor»"
+
+#: repack.c
+#, c-format
+msgid "pack prefix %s does not begin with objdir %s"
+msgstr "el prefix de paquet %s no comença amb objdir %s"
+
+#: repack.c
+#, c-format
+msgid "renaming pack to '%s' failed"
+msgstr "el canvi del nom a «%s» ha fallat"
+
+#: repack.c
+#, c-format
+msgid "pack-objects did not write a '%s' file for pack %s-%s"
+msgstr ""
+"els objectes de paquet no han escrit a un fitxer «%s» per al paquet %s-%s"
+
+#: repack.c sequencer.c
+#, c-format
+msgid "could not unlink: %s"
+msgstr "no s'ha pogut desenllaçar: «%s»"
+
 #: replace-object.c
 #, c-format
 msgid "bad replace ref name: %s"
@@ -25973,6 +27835,46 @@
 msgid "replace depth too high for object %s"
 msgstr "la profunditat de reemplaçament és massa alta per l'objecte %s"
 
+#: replay.c
+#, c-format
+msgid "'%s' is not a valid commit-ish for %s"
+msgstr "«%s» no és una comissió (o similar) vàlida per a %s"
+
+#: replay.c
+#, c-format
+msgid "argument to %s must be a reference"
+msgstr "l'argument de %s ha de ser una referència"
+
+#: replay.c
+#, c-format
+msgid ""
+"'%s' cannot be used with multiple revision ranges because the ordering would "
+"be ill-defined"
+msgstr ""
+"no es pot usar «%s» amb rangs múltiples de revisió perquè l'ordre no estaria "
+"ben definit"
+
+#: replay.c
+msgid "need some commits to replay"
+msgstr "calen algunes comissions per tornar a reproduir"
+
+#: replay.c
+msgid "all positive revisions given must be references"
+msgstr "totes les revisions positives que s'han donat han de ser referències"
+
+#: replay.c
+msgid "'--ref' cannot be used with multiple revision ranges"
+msgstr "«--ref» no es pot usar amb rangs múltiples de revisió"
+
+#: replay.c sequencer.c
+#, c-format
+msgid "'%s' is not a valid refname"
+msgstr "«%s» no és un nom de referència vàlid"
+
+#: repository.c
+msgid "compatibility hash algorithm support requires Rust"
+msgstr "el suport per a l'algorisme de compatibilitat de hash requereix Rust"
+
 #: rerere.c
 msgid "corrupt MERGE_RR"
 msgstr "MERGE_RR corrupte"
@@ -26526,7 +28428,7 @@
 "run \"git cherry-pick --abort\"."
 msgstr ""
 "Després de resoldre els conflictes, marqueu-los amb\n"
-"«git add/rm <pathspec>», llavors executeu\n"
+"«git add/rm <especificació-camí>», llavors executeu\n"
 "«git cherry-pick --continue».\n"
 "Podeu també ometre aquesta comissió amb «git cherry-pick --skip».\n"
 "Per a interrompre i tornar a l'estat anterior abans de «git cherry-pick»,\n"
@@ -26542,7 +28444,7 @@
 "run \"git revert --abort\"."
 msgstr ""
 "Després de resoldre els conflictes, marqueu-los amb\n"
-"«git add/rm <pathspec>», llavors executeu\n"
+"«git add/rm <especificació-camí>», llavors executeu\n"
 "«git revert --continue».\n"
 "Podeu també ometre aquesta comissió amb «git revert --skip».\n"
 "Per a interrompre i tornar a l'estat anterior abans de «git revert»,\n"
@@ -26883,11 +28785,6 @@
 
 #: sequencer.c
 #, c-format
-msgid "'%s' is not a valid refname"
-msgstr "«%s» no és un nom de referència vàlid"
-
-#: sequencer.c
-#, c-format
 msgid "update-ref requires a fully qualified refname e.g. refs/heads/%s"
 msgstr ""
 "«update-ref» requereix un refname plenament qualificat, p. ex. refs/heads/%s"
@@ -26995,6 +28892,18 @@
 msgstr "no es pot revertir durant un «cherry pick»."
 
 #: sequencer.c
+msgid "trailers file contains empty line"
+msgstr "el fitxer de «trailers» conté una línia buida"
+
+#: sequencer.c
+msgid "trailers file is empty"
+msgstr "el fitxer de «trailers» és buit"
+
+#: sequencer.c
+msgid "cannot read trailers files"
+msgstr "no es poden llegir els fitxers de «trailers»"
+
+#: sequencer.c
 msgid "unusable squash-onto"
 msgstr "«squash-onto» no usable"
 
@@ -27058,7 +28967,9 @@
 
 #: sequencer.c
 msgid "You seem to have moved HEAD. Not rewinding, check your HEAD!"
-msgstr "Sembla que heu mogut HEAD. No es fa el rebobinat, comproveu-ho HEAD"
+msgstr ""
+"Sembla que heu mogut el HEAD. No es fa el rebobinat, comproveu el vostre "
+"HEAD!"
 
 #: sequencer.c
 msgid "no revert in progress"
@@ -27169,11 +29080,6 @@
 msgstr "nom d'etiqueta no permès: «%.*s»"
 
 #: sequencer.c
-#, c-format
-msgid "could not resolve '%s'"
-msgstr "no s'ha pogut resoldre «%s»"
-
-#: sequencer.c
 msgid "writing fake root commit"
 msgstr "s'està escrivint una comissió arrel falsa"
 
@@ -27570,10 +29476,6 @@
 msgstr "no es pot canviar de directori a «%s»"
 
 #: setup.c
-msgid "cannot come back to cwd"
-msgstr "no es pot tornar al directori de treball actual"
-
-#: setup.c
 #, c-format
 msgid "failed to stat '%*s%s%s'"
 msgstr "s'ha produït un error en fer stat a «%*s%s%s»"
@@ -27597,6 +29499,16 @@
 "\tgit config --global --add safe.directory %s"
 
 #: setup.c
+#, c-format
+msgid "error reading '%s'"
+msgstr "error en llegir %s"
+
+#: setup.c
+#, c-format
+msgid "not a regular file: '%s'"
+msgstr "no és un fitxer normal: %s"
+
+#: setup.c
 msgid "Unable to read current working directory"
 msgstr "No s'ha pogut llegir el directori de treball actual"
 
@@ -27628,6 +29540,11 @@
 
 #: setup.c
 #, c-format
+msgid "unknown ref storage format: '%s'"
+msgstr "el format d'emmagatzematge de referència «%s» és desconegut"
+
+#: setup.c
+#, c-format
 msgid ""
 "problem with core.sharedRepository filemode value (0%.3o).\n"
 "The owner of files must always have read and write permissions."
@@ -27768,57 +29685,83 @@
 msgid "bad %s format: %%%.*s"
 msgstr "format %s incorrecte: %%%.*s"
 
-#. TRANSLATORS: IEC 80000-13:2008 gibibyte
 #: strbuf.c
 #, c-format
-msgid "%u.%2.2u GiB"
-msgstr "%u.%2.2u GiB"
+msgid "%u.%2.2u"
+msgstr "%u.%2.2u"
 
-#. TRANSLATORS: IEC 80000-13:2008 gibibyte/second
+#. TRANSLATORS: SI decimal prefix symbol for 10^9
 #: strbuf.c
-#, c-format
-msgid "%u.%2.2u GiB/s"
-msgstr "%u.%2.2u GiB/s"
+msgid "G"
+msgstr "G"
 
-#. TRANSLATORS: IEC 80000-13:2008 mebibyte
+#. TRANSLATORS: SI decimal prefix symbol for 10^6
 #: strbuf.c
-#, c-format
-msgid "%u.%2.2u MiB"
-msgstr "%u.%2.2u MiB"
+msgid "M"
+msgstr "M"
 
-#. TRANSLATORS: IEC 80000-13:2008 mebibyte/second
+#. TRANSLATORS: SI decimal prefix symbol for 10^3
 #: strbuf.c
-#, c-format
-msgid "%u.%2.2u MiB/s"
-msgstr "%u.%2.2u MiB/s"
+msgid "k"
+msgstr "k"
 
-#. TRANSLATORS: IEC 80000-13:2008 kibibyte
+#. TRANSLATORS: IEC 80000-13:2008 gibibyte/second and gibibyte
 #: strbuf.c
-#, c-format
-msgid "%u.%2.2u KiB"
-msgstr "%u.%2.2u KiB"
+msgid "GiB/s"
+msgstr "GiB/s"
 
-#. TRANSLATORS: IEC 80000-13:2008 kibibyte/second
 #: strbuf.c
-#, c-format
-msgid "%u.%2.2u KiB/s"
-msgstr "%u.%2.2u KiB/s"
+msgid "GiB"
+msgstr "GiB"
 
-#. TRANSLATORS: IEC 80000-13:2008 byte
+#. TRANSLATORS: IEC 80000-13:2008 mebibyte/second and mebibyte
 #: strbuf.c
-#, c-format
-msgid "%u byte"
-msgid_plural "%u bytes"
-msgstr[0] "%u octet"
-msgstr[1] "%u octets"
+msgid "MiB/s"
+msgstr "MiB/s"
+
+#: strbuf.c
+msgid "MiB"
+msgstr "MiB"
+
+#. TRANSLATORS: IEC 80000-13:2008 kibibyte/second and kibibyte
+#: strbuf.c
+msgid "KiB/s"
+msgstr "KiB/s"
+
+#: strbuf.c
+msgid "KiB"
+msgstr "KiB"
+
+#. TRANSLATORS: IEC 80000-13:2008 byte/second and byte
+#: strbuf.c
+msgid "B/s"
+msgstr "B/s"
+
+#: strbuf.c
+msgid "B"
+msgstr "B"
 
 #. TRANSLATORS: IEC 80000-13:2008 byte/second
 #: strbuf.c
+msgid "byte/s"
+msgid_plural "bytes/s"
+msgstr[0] "octet/s"
+msgstr[1] "octets/s"
+
+#. TRANSLATORS: IEC 80000-13:2008 byte
+#: strbuf.c
+msgid "byte"
+msgid_plural "bytes"
+msgstr[0] "octet"
+msgstr[1] "octets"
+
+#. TRANSLATORS: The first argument is the number string. The second
+#. argument is the unit string (i.e. "12.34 MiB/s").
+#.
+#: strbuf.c
 #, c-format
-msgid "%u byte/s"
-msgid_plural "%u bytes/s"
-msgstr[0] "%u octet/s"
-msgstr[1] "%u octets/s"
+msgid "%s %s"
+msgstr "%s %s"
 
 #: submodule-config.c
 #, c-format
@@ -28025,11 +29968,6 @@
 
 #: submodule.c
 #, c-format
-msgid "refusing to move '%s' into an existing git dir"
-msgstr "s'ha refusat moure «%s» a un directori git existent"
-
-#: submodule.c
-#, c-format
 msgid ""
 "Migrating git directory of '%s%s' from\n"
 "'%s' to\n"
@@ -28048,6 +29986,32 @@
 msgid "ls-tree returned unexpected return code %d"
 msgstr "ls-tree ha retornat un codi de retorn %d no esperat"
 
+#: submodule.c
+#, c-format
+msgid ""
+"the 'submodule.%s.gitdir' config does not exist for module '%s'. Please "
+"ensure it is set, for example by running something like: 'git config "
+"submodule.%s.gitdir .git/modules/%s'. For details see the "
+"extensions.submodulePathConfig documentation."
+msgstr ""
+"la configuració «submodule.%s.gitdir» no existeix per al mòdul «%s». Si us "
+"plau, assegureu-vos que està establerta, per exemple, executant una ordre de "
+"l'estil de «git config submodule.%s.gitdir .git/modules/%s». Per a més "
+"detalls, llegiu la documentació de extensions.submodulePathConfig ."
+
+#: submodule.c
+msgid ""
+"enabling extensions.submodulePathConfig might fix the following error, if "
+"it's not already enabled."
+msgstr ""
+"habilitar extensions.submodulePathConfig podria solucionar l'error següent, "
+"si no està ja habilitat."
+
+#: submodule.c
+#, c-format
+msgid "refusing to create/use '%s' in another submodule's  git dir."
+msgstr "s'ha rebutjat crear/usar «%s» en el directori git d'un altre submòdul."
+
 #: symlinks.c
 #, c-format
 msgid "failed to lstat '%s'"
@@ -28120,6 +30084,11 @@
 msgstr "hi ha massa comissions marcades com abastables"
 
 #: t/helper/test-read-midx.c
+#, c-format
+msgid "could not find MIDX with checksum %s"
+msgstr "no s'ha pogut trobar el MIDX amb suma de verificació %s"
+
+#: t/helper/test-read-midx.c
 msgid "could not determine MIDX preferred pack"
 msgstr "no s'ha pogut determinar el paquet preferit MIDX"
 
@@ -28189,10 +30158,6 @@
 msgstr "nombre de peticions per fil"
 
 #: t/helper/test-simple-ipc.c
-msgid "byte"
-msgstr "octet"
-
-#: t/helper/test-simple-ipc.c
 msgid "ballast character"
 msgstr "caràcter de llast"
 
@@ -28245,6 +30210,34 @@
 msgid "empty trailer token in trailer '%.*s'"
 msgstr "testimoni de «trailer» buit en el «trailer» «%.*s»"
 
+#: trailer.c
+msgid "empty --trailer argument"
+msgstr "argument de --trailer buit"
+
+#: trailer.c
+#, c-format
+msgid "invalid trailer '%s': missing key before separator"
+msgstr "«trailer» invàlid «%s»: falta la clau davant del separador"
+
+#: trailer.c wrapper.c
+#, c-format
+msgid "could not stat %s"
+msgstr "no s'ha pogut fer stat a %s"
+
+#: trailer.c
+#, c-format
+msgid "file %s is not a regular file"
+msgstr "el fitxer %s no és un fitxer regular"
+
+#: trailer.c
+#, c-format
+msgid "file %s is not writable by user"
+msgstr "el fitxer %s no és gravable per l'usuari"
+
+#: trailer.c
+msgid "could not write to temporary file"
+msgstr "no s'ha pogut escriure al fitxer temporal"
+
 #: transport-helper.c
 msgid "full write to remote helper failed"
 msgstr "l'escriptura completa a l'ajudant remot ha fallat"
@@ -28886,18 +30879,33 @@
 
 #: usage.c
 #, c-format
+msgid "'%s' is nominated for removal.\n"
+msgstr "«%s» està nominat per a ser eliminat.\n"
+
+#: usage.c
+#, c-format
 msgid ""
-"'%s' is nominated for removal.\n"
-"If you still use this command, please add an extra\n"
-"option, '--i-still-use-this', on the command line\n"
-"and let us know you still use it by sending an e-mail\n"
-"to <git@vger.kernel.org>.  Thanks.\n"
+"If you still use this command, here's what you can do:\n"
+"\n"
+"- read https://git-scm.com/docs/BreakingChanges.html\n"
+"- check if anyone has discussed this on the mailing\n"
+"  list and if they came up with something that can\n"
+"  help you: https://lore.kernel.org/git/?q=%s\n"
+"- send an email to <git@vger.kernel.org> to let us\n"
+"  know that you still use this command and were unable\n"
+"  to determine a suitable replacement\n"
+"\n"
 msgstr ""
-"«%s» està nominat per a la seva supressió.\n"
-"Si encara feu servir aquesta ordre, afegiu-hi l'opció\n"
-"addicional, «--i-still-use-this», a la línia d'ordres\n"
-"i feu-nos saber que encara l'useu enviant un correu electrònic\n"
-"a <git@vger.kernel.org>.  Gràcies.\n"
+"Si encara useu aquesta ordre, podeu fer això:\n"
+"\n"
+"- llegiu https://git-scm.com/docs/BreakingChanges.html\n"
+"- comproveu si algú ha discutit això en la llista\n"
+"  de correu i si se'ls ha acudit alguna cosa que us\n"
+"  pugui ajudar: https://lore.kernel.org/git/?q=%s\n"
+"- envieu un email a <git@vger.kernel.org> per a fer-nos\n"
+"  saber que encara useu aquesta ordre i no heu pogut\n"
+"  determinar una alternativa adequada\n"
+"\n"
 
 #: usage.c
 msgid "refusing to run without --i-still-use-this"
@@ -29893,8 +31901,17 @@
 "Encoding.\n"
 
 #: git-send-email.perl
-msgid "Which 8bit encoding should I declare [UTF-8]? "
-msgstr "Quina codificació de 8 bits hauria de declarar [UTF-8]? "
+msgid "Declare which 8bit encoding to use [default: UTF-8]? "
+msgstr ""
+"Declareu quina codificació de 8 bits hauria d'utilitzar [per defecte: "
+"UTF-8]? "
+
+#: git-send-email.perl
+#, perl-format
+msgid "'%s' does not appear to be a valid charset name. Use it anyway [y/N]? "
+msgstr ""
+"«%s» no sembla ser un nom de joc de caràcters vàlid. Cal usar-lo de tota "
+"manera [y/N]? "
 
 #: git-send-email.perl
 #, perl-format
@@ -29943,6 +31960,11 @@
 msgstr "el camí CA «%s» no existeix"
 
 #: git-send-email.perl
+#, perl-format
+msgid "Only client key \"%s\" specified"
+msgstr "Només s'ha especificar la clau de client «%s»"
+
+#: git-send-email.perl
 msgid ""
 "    The Cc list above has been expanded by additional\n"
 "    addresses found in the patch commit message. By default\n"
@@ -29979,6 +32001,10 @@
 msgstr "Requereix resposta en enviar el correu"
 
 #: git-send-email.perl
+msgid "The destination IMAP folder is not properly defined."
+msgstr "La carpeta IMAP de destinació no està correctament definida."
+
+#: git-send-email.perl
 msgid "The required SMTP server is not properly defined."
 msgstr "El servidor SMTP requerit no està correctament definit."
 
@@ -30133,438 +32159,3 @@
 #, perl-format
 msgid "Do you really want to send %s? [y|N]: "
 msgstr "Esteu segur que voleu enviar %s? [y|N]: "
-
-#~ msgid "start-after"
-#~ msgstr "start-after"
-
-#~ msgid "compact-summary"
-#~ msgstr "compact-summary"
-
-#~ msgid "git cat-file (-t | -s) [--allow-unknown-type] <object>"
-#~ msgstr "git cat-file (-t | -s) [--allow-unknown-type] <objecte>"
-
-#~ msgid "allow -s and -t to work with broken/corrupt objects"
-#~ msgstr "permet que -s i -t funcionin amb objectes trencats/malmesos"
-
-#, c-format
-#~ msgid "Could not find remote branch %s to clone."
-#~ msgstr "No s'ha pogut trobar la branca remota %s per a clonar."
-
-#, c-format
-#~ msgid ""
-#~ "more than %i tags found; listed %i most recent\n"
-#~ "gave up search at %s\n"
-#~ msgstr ""
-#~ "s'han trobat més de %i etiquetes: s'han llistat les %i més recents\n"
-#~ "s'ha renunciat la cerca a %s\n"
-
-#, c-format
-#~ msgid "   (%s will become dangling)"
-#~ msgstr "   (%s es tornarà despenjat)"
-
-#, c-format
-#~ msgid "   (%s has become dangling)"
-#~ msgstr "   (%s s'ha quedat despenjat)"
-
-#, c-format
-#~ msgid "%s: object is of unknown type '%s': %s"
-#~ msgstr "%s: l'objecte és de tipus desconegut «%s»: %s"
-
-#~ msgid "use at most one of --auto and --schedule=<frequency>"
-#~ msgstr "usa com a màxim un entre --auto i --schedule=<freqüència>"
-
-#, c-format
-#~ msgid "Final output: %d %s\n"
-#~ msgstr "Sortida final: %d %s\n"
-
-#, c-format
-#~ msgid "merging cannot continue; got unclean result of %d"
-#~ msgstr "la fusió no pot continuar; s'ha obtingut un resultat no net de %d"
-
-#, c-format
-#~ msgid "%d (FSCK_IGNORE?) should never trigger this callback"
-#~ msgstr "%d (FSCK_IGNORE?) no hauria d'activar mai aquesta crida de retorn"
-
-#~ msgid ""
-#~ "git pack-objects --stdout [<options>] [< <ref-list> | < <object-list>]"
-#~ msgstr ""
-#~ "git pack-objects --stdout [<opcions>] [< <ref-list> | < <object-list>]"
-
-#~ msgid ""
-#~ "git pack-objects [<options>] <base-name> [< <ref-list> | < <object-list>]"
-#~ msgstr ""
-#~ "git pack-objects [<opcions>] <base-name> [< <ref-list> | < <object-list>]"
-
-#~ msgid "cannot use --stdin-packs with --cruft"
-#~ msgstr "no es pot --stdin-packs amb --cruft"
-
-#, c-format
-#~ msgid "%s points nowhere!"
-#~ msgstr "%s no apunta a enlloc"
-
-#~ msgid "--onto and --advance are incompatible"
-#~ msgstr "--onto i --advance són incompatibles"
-
-#, c-format
-#~ msgid "unreachable: invalid reference: %s"
-#~ msgstr "no accessible: referència no vàlida: %s"
-
-#~ msgid "Public key pinning not supported with cURL < 7.39.0"
-#~ msgstr "No s'admet la fixació de clau pública amb cURL < 7.39.0"
-
-#~ msgid "CURLSSLOPT_NO_REVOKE not supported with cURL < 7.44.0"
-#~ msgstr "CURLSSLOPT_NO_REVOKE no està admès amb cURL < 7.44.0"
-
-#~ msgid "(bad commit)\n"
-#~ msgstr "(comissió errònia)\n"
-
-#, c-format
-#~ msgid "add_cacheinfo failed for path '%s'; merge aborting."
-#~ msgstr "add_cacheinfo ha fallat per al camí «%s»; interrompent la fusió."
-
-#, c-format
-#~ msgid "add_cacheinfo failed to refresh for path '%s'; merge aborting."
-#~ msgstr ""
-#~ "add_cacheinfo ha fallat al refrescar el camí «%s»; interrompent la fusió."
-
-#, c-format
-#~ msgid "failed to create path '%s'%s"
-#~ msgstr "s'ha produït un error en crear el camí «%s»%s"
-
-#, c-format
-#~ msgid "Removing %s to make room for subdirectory\n"
-#~ msgstr "S'està eliminant %s per a fer espai per al subdirectori\n"
-
-#~ msgid ": perhaps a D/F conflict?"
-#~ msgstr ": potser un conflicte D/F?"
-
-#, c-format
-#~ msgid "refusing to lose untracked file at '%s'"
-#~ msgstr "s'està refusant perdre el fitxer no seguit a «%s»"
-
-#, c-format
-#~ msgid "blob expected for %s '%s'"
-#~ msgstr "blob esperat per a %s «%s»"
-
-#, c-format
-#~ msgid "failed to open '%s': %s"
-#~ msgstr "s'ha produït un error en obrir «%s»: %s"
-
-#, c-format
-#~ msgid "failed to symlink '%s': %s"
-#~ msgstr "s'ha produït un error en fer l'enllaç simbòlic «%s»: %s"
-
-#, c-format
-#~ msgid "do not know what to do with %06o %s '%s'"
-#~ msgstr "no se sap què fer amb %06o %s «%s»"
-
-#, c-format
-#~ msgid "Failed to merge submodule %s (repository corrupt)"
-#~ msgstr "No s'ha pogut fusionar el submòdul %s (repositori malmès)"
-
-#, c-format
-#~ msgid "Fast-forwarding submodule %s to the following commit:"
-#~ msgstr "Avançament ràpid del submòdul %s a la següent comissió:"
-
-#, c-format
-#~ msgid "Fast-forwarding submodule %s"
-#~ msgstr "Avançament ràpid al submòdul %s"
-
-#, c-format
-#~ msgid "Failed to merge submodule %s (merge following commits not found)"
-#~ msgstr ""
-#~ "Ha fallat en fusionar el submòdul %s (no s'ha trobat les comissions "
-#~ "següents)"
-
-#, c-format
-#~ msgid "Failed to merge submodule %s (not fast-forward)"
-#~ msgstr ""
-#~ "S'ha produït un error en fusionar el submòdul %s (sense avançament ràpid)"
-
-#~ msgid "Found a possible merge resolution for the submodule:\n"
-#~ msgstr "S'ha trobat una possible resolució de fusió pel submòdul:\n"
-
-#, c-format
-#~ msgid ""
-#~ "If this is correct simply add it to the index for example\n"
-#~ "by using:\n"
-#~ "\n"
-#~ "  git update-index --cacheinfo 160000 %s \"%s\"\n"
-#~ "\n"
-#~ "which will accept this suggestion.\n"
-#~ msgstr ""
-#~ "Si això és correcte simplement afegiu-ho a l'índex per exemple\n"
-#~ "utilitzant:\n"
-#~ "\n"
-#~ "  git update-index --cacheinfo 160000 %s «%s»\n"
-#~ "\n"
-#~ "que acceptarà aquest suggeriment.\n"
-
-#, c-format
-#~ msgid "Failed to merge submodule %s (multiple merges found)"
-#~ msgstr ""
-#~ "S'ha produït un error en fusionar el submòdul %s (s'han trobat múltiples "
-#~ "fusions)"
-
-#~ msgid "failed to execute internal merge"
-#~ msgstr "no s'ha pogut executar la fusió interna"
-
-#, c-format
-#~ msgid "unable to add %s to database"
-#~ msgstr "no s'ha pogut afegir %s a la base de dades"
-
-#, c-format
-#~ msgid "Error: Refusing to lose untracked file at %s; writing to %s instead."
-#~ msgstr ""
-#~ "Error: s'està refusant perdre el fitxer no seguit a %s; en comptes s'ha "
-#~ "escrit a %s."
-
-#, c-format
-#~ msgid ""
-#~ "CONFLICT (%s/delete): %s deleted in %s and %s in %s. Version %s of %s "
-#~ "left in tree."
-#~ msgstr ""
-#~ "CONFLICTE: (%s/supressió): %s suprimit en %s i %s en %s. La versió %s de "
-#~ "%s s'ha deixat en l'arbre."
-
-#, c-format
-#~ msgid ""
-#~ "CONFLICT (%s/delete): %s deleted in %s and %s to %s in %s. Version %s of "
-#~ "%s left in tree."
-#~ msgstr ""
-#~ "CONFLICTE: (%s/supressió): %s suprimit en %s i %s a %s en %s. La versió "
-#~ "%s de %s s'ha deixat en l'arbre."
-
-#, c-format
-#~ msgid ""
-#~ "CONFLICT (%s/delete): %s deleted in %s and %s in %s. Version %s of %s "
-#~ "left in tree at %s."
-#~ msgstr ""
-#~ "CONFLICTE: (%s/supressió): %s suprimit en %s i %s en %s. La versió %s de "
-#~ "%s s'ha deixat en l'arbre a %s."
-
-#, c-format
-#~ msgid ""
-#~ "CONFLICT (%s/delete): %s deleted in %s and %s to %s in %s. Version %s of "
-#~ "%s left in tree at %s."
-#~ msgstr ""
-#~ "CONFLICTE: (%s/supressió): %s suprimit en %s i %s a %s en %s. La versió "
-#~ "%s de %s s'ha deixat en l'arbre a %s."
-
-#~ msgid "rename"
-#~ msgstr "canvi de nom"
-
-#~ msgid "renamed"
-#~ msgstr "canviat de nom"
-
-#, c-format
-#~ msgid "Refusing to lose dirty file at %s"
-#~ msgstr "S'està refusant a perdre el fitxer brut a %s"
-
-#, c-format
-#~ msgid "Refusing to lose untracked file at %s, even though it's in the way."
-#~ msgstr ""
-#~ "S'està refusant perdre el fitxer no seguit a «%s», malgrat que està en "
-#~ "mig de l'operació."
-
-#, c-format
-#~ msgid "CONFLICT (rename/add): Rename %s->%s in %s.  Added %s in %s"
-#~ msgstr ""
-#~ "CONFLICTE (canvi de nom/afegiment): Canvi de nom %s->%s a %s.  S'ha "
-#~ "afegit %s a %s"
-
-#, c-format
-#~ msgid "%s is a directory in %s adding as %s instead"
-#~ msgstr "%s és un directori en %s; s'està afegint com a %s en lloc d'això"
-
-#, c-format
-#~ msgid "Refusing to lose untracked file at %s; adding as %s instead"
-#~ msgstr ""
-#~ "S'està refusant perdre el fitxer no seguit a %s; en comptes, s'està "
-#~ "afegint com a %s"
-
-#, c-format
-#~ msgid ""
-#~ "CONFLICT (rename/rename): Rename \"%s\"->\"%s\" in branch \"%s\" rename "
-#~ "\"%s\"->\"%s\" in \"%s\"%s"
-#~ msgstr ""
-#~ "CONFLICTE (canvi de nom/canvi de nom): Canvi de nom «%s»->«%s» en la "
-#~ "branca «%s» canvi de nom «%s»->«%s» en «%s»%s"
-
-#~ msgid " (left unresolved)"
-#~ msgstr " (deixat sense resolució)"
-
-#, c-format
-#~ msgid "CONFLICT (rename/rename): Rename %s->%s in %s. Rename %s->%s in %s"
-#~ msgstr ""
-#~ "CONFLICTE (canvi de nom/canvi de nom): Canvi de nom %s->%s en %s. Canvi "
-#~ "de nom %s->%s en %s"
-
-#, c-format
-#~ msgid ""
-#~ "CONFLICT (directory rename split): Unclear where to place %s because "
-#~ "directory %s was renamed to multiple other directories, with no "
-#~ "destination getting a majority of the files."
-#~ msgstr ""
-#~ "CONFLICTE (divisió de canvi de nom de directori): no està clar on "
-#~ "col·locar %s perquè el directori %s s'han canviat de nom a múltiples "
-#~ "altres directoris, sense una destinació per a la majoria dels fitxers."
-
-#, c-format
-#~ msgid ""
-#~ "CONFLICT (rename/rename): Rename directory %s->%s in %s. Rename directory "
-#~ "%s->%s in %s"
-#~ msgstr ""
-#~ "CONFLICTE (canvi de nom/canvi de nom): canvi de nom %s->%s en %s. Canvi "
-#~ "de nom de directori %s->%s en %s"
-
-#, c-format
-#~ msgid "cannot read object %s"
-#~ msgstr "no es pot llegir l'objecte %s"
-
-#, c-format
-#~ msgid "object %s is not a blob"
-#~ msgstr "l'objecte %s no és un blob"
-
-#~ msgid "modify"
-#~ msgstr "modificació"
-
-#~ msgid "modified"
-#~ msgstr "modificat"
-
-#, c-format
-#~ msgid "Skipped %s (merged same as existing)"
-#~ msgstr "S'ha omès %s (el fusionat és igual a l'existent)"
-
-#, c-format
-#~ msgid "Adding as %s instead"
-#~ msgstr "S'està afegint com a %s en lloc d'això"
-
-#, c-format
-#~ msgid "Removing %s"
-#~ msgstr "S'està eliminant %s"
-
-#~ msgid "file/directory"
-#~ msgstr "fitxer/directori"
-
-#~ msgid "directory/file"
-#~ msgstr "directori/fitxer"
-
-#, c-format
-#~ msgid ""
-#~ "CONFLICT (%s): There is a directory with name %s in %s. Adding %s as %s"
-#~ msgstr ""
-#~ "CONFLICTE (%s): Hi ha un directori amb nom %s en %s. S'està afegint %s "
-#~ "com a %s"
-
-#, c-format
-#~ msgid "Adding %s"
-#~ msgstr "S'està afegint %s"
-
-#, c-format
-#~ msgid "CONFLICT (add/add): Merge conflict in %s"
-#~ msgstr "CONFLICTE (afegiment/afegiment): Conflicte de fusió en %s"
-
-#, c-format
-#~ msgid "merging of trees %s and %s failed"
-#~ msgstr "la fusió dels arbres %s i %s ha fallat"
-
-#~ msgid "Merging:"
-#~ msgstr "S'està fusionant:"
-
-#, c-format
-#~ msgid "found %u common ancestor:"
-#~ msgid_plural "found %u common ancestors:"
-#~ msgstr[0] "s'ha trobat %u avantpassat en comú:"
-#~ msgstr[1] "s'han trobat %u avantpassats en comú:"
-
-#~ msgid "merge returned no commit"
-#~ msgstr "la fusió no ha retornat cap comissió"
-
-#~ msgid "cannot write incremental MIDX with bitmap"
-#~ msgstr "no es pot escriure un MIDX incremental amb mapa de bits"
-
-#~ msgid "trying to write commit not in index"
-#~ msgstr "s'està intentant no escriure la comissió a l'índex"
-
-#, c-format
-#~ msgid "preferred pack (%s) is invalid"
-#~ msgstr "el paquet preferit (%s) no és vàlid"
-
-#, c-format
-#~ msgid "key '%s' of pattern had no '*'"
-#~ msgstr "la clau «%s» del patró no té «*»"
-
-#~ msgid "revision walk setup failed\n"
-#~ msgstr "la configuració del recorregut de revisions ha fallat\n"
-
-#, c-format
-#~ msgid "unable to parse contact: %s"
-#~ msgstr "no s'ha pogut analitzar el contacte: %s"
-
-#, c-format
-#~ msgid "truncating .rej filename to %.*s.rej"
-#~ msgstr "s'està truncant el nom del fitxer .rej a %.*s.rej"
-
-#~ msgid ""
-#~ "the add.interactive.useBuiltin setting has been removed!\n"
-#~ "See its entry in 'git help config' for details."
-#~ msgstr ""
-#~ "s'ha eliminat la configuració add.interactive.useBuiltin\n"
-#~ "Per a més detalls, vegeu la seva entrada a «git help config»."
-
-#~ msgid ""
-#~ "Use -f if you really want to add them.\n"
-#~ "Turn this message off by running\n"
-#~ "\"git config advice.addIgnoredFile false\""
-#~ msgstr ""
-#~ "Utilitzeu -f si realment voleu afegir-los.\n"
-#~ "Desactiveu aquest missatge executant\n"
-#~ "«git config advice.addIgnoredFile false»"
-
-#~ msgid ""
-#~ "Maybe you wanted to say 'git add .'?\n"
-#~ "Turn this message off by running\n"
-#~ "\"git config advice.addEmptyPathspec false\""
-#~ msgstr ""
-#~ "Potser voleu dir «git add .»?\n"
-#~ "Desactiveu aquest missatge executant\n"
-#~ "«git config advice.addEmptyPathspec false»"
-
-#~ msgid "git archive: Remote with no URL"
-#~ msgstr "git archive: Remot sense URL"
-
-#~ msgid ""
-#~ "clean.requireForce defaults to true and neither -i, -n, nor -f given; "
-#~ "refusing to clean"
-#~ msgstr ""
-#~ "clean.requireForce és per defecte cert i ni -i, -n ni -f s'han indicat; "
-#~ "refusant netejar"
-
-#~ msgid "only one action at a time"
-#~ msgstr "només una acció cada cop"
-
-#~ msgid "use [RFC PATCH] instead of [PATCH]"
-#~ msgstr "useu [RFC PATCH] en comptes de [PATCH]"
-
-#, c-format
-#~ msgid "bad ls-files format: element '%s' does not start with '('"
-#~ msgstr "format incorrecte del ls-files: l'element «%s» no comença amb «(»"
-
-#, c-format
-#~ msgid "bad ls-files format: element '%s' does not end in ')'"
-#~ msgstr "format incorrecte del ls-files: l'element «%s» no acaba amb «)»"
-
-#, c-format
-#~ msgid "bad ls-files format: %%%.*s"
-#~ msgstr "format incorrecte de ls-files: %%%.*s"
-
-#~ msgid "keep redundant, empty commits"
-#~ msgstr "retén les comissions redundants i buides"
-
-#~ msgid "core.commentChar should only be one ASCII character"
-#~ msgstr "core.commentChar només hauria de ser un caràcter ASCII"
-
-#, c-format
-#~ msgid "remote '%s' has no configured URL"
-#~ msgstr "el remot «%s» no té cap URL configurat"
diff --git a/po/es.po b/po/es.po
index 1ff5ff3..aa1bb9b 100644
--- a/po/es.po
+++ b/po/es.po
@@ -391,8 +391,8 @@
 #, c-format, perl-format
 msgid "Apply mode change to index and worktree [y,n,q,a,d%s,?]? "
 msgstr ""
-"¿Aplicar cambio de modo para el índice y el árbol de trabajo [y,n,q,a,"
-"d%s,?]? "
+"¿Aplicar cambio de modo para el índice y el árbol de trabajo "
+"[y,n,q,a,d%s,?]? "
 
 #, c-format, perl-format
 msgid "Apply deletion to index and worktree [y,n,q,a,d%s,?]? "
@@ -2294,9 +2294,9 @@
 "=<term>] [--no-checkout] [--first-parent] [<bad> [<good>...]] [--] "
 "[<paths>...]"
 msgstr ""
-"git bisect--helper --bisect-start [--term-{new,bad}=<término> --term-{old,"
-"good}=<término>] [--no-checkout] [--first-parent] [<malo> [<bueno>...]] [--] "
-"[<rutas>...]"
+"git bisect--helper --bisect-start [--term-{new,bad}=<término> --term-"
+"{old,good}=<término>] [--no-checkout] [--first-parent] [<malo> [<bueno>...]] "
+"[--] [<rutas>...]"
 
 msgid "git bisect--helper --bisect-state (bad|new) [<rev>]"
 msgstr "git bisect--helper --bisect-state (bad|new) [<rev>]"
@@ -2983,11 +2983,11 @@
 msgstr "¡HEAD no encontrado dentro de refs/heads!"
 
 msgid ""
-"branch with --recurse-submodules can only be used if submodule."
-"propagateBranches is enabled"
+"branch with --recurse-submodules can only be used if "
+"submodule.propagateBranches is enabled"
 msgstr ""
-"branch con --recurse-submodules solo se puede usar si submodule."
-"propagateBranches está habilitado"
+"branch con --recurse-submodules solo se puede usar si "
+"submodule.propagateBranches está habilitado"
 
 msgid "--recurse-submodules can only be used to create branches"
 msgstr "--recurse-submodules solo se puede usar para crear ramas"
@@ -5983,11 +5983,11 @@
 msgstr "el protocolo no soporta --negotiate-only, saliendo"
 
 msgid ""
-"--filter can only be used with the remote configured in extensions."
-"partialclone"
+"--filter can only be used with the remote configured in "
+"extensions.partialclone"
 msgstr ""
-"--filter solo puede ser usado con el remoto configurado en extensions."
-"partialclone"
+"--filter solo puede ser usado con el remoto configurado en "
+"extensions.partialclone"
 
 msgid "--atomic can only be used when fetching from one remote"
 msgstr "--atomic solo se puede usar cuando se busca desde un control remoto"
@@ -8914,8 +8914,8 @@
 
 msgid "disabling bitmap writing, packs are split due to pack.packSizeLimit"
 msgstr ""
-"deshabilitando escritura bitmap, paquetes son divididos debido a pack."
-"packSizeLimit"
+"deshabilitando escritura bitmap, paquetes son divididos debido a "
+"pack.packSizeLimit"
 
 msgid "Writing objects"
 msgstr "Escribiendo objetos"
@@ -9489,8 +9489,8 @@
 msgid ""
 "\n"
 "To avoid automatically configuring upstream branches when their name\n"
-"doesn't match the local branch, see option 'simple' of branch."
-"autoSetupMerge\n"
+"doesn't match the local branch, see option 'simple' of "
+"branch.autoSetupMerge\n"
 "in 'git help config'.\n"
 msgstr ""
 "\n"
diff --git a/po/fr.po b/po/fr.po
index 780bc7b..ab3827a 100644
--- a/po/fr.po
+++ b/po/fr.po
@@ -87,8 +87,8 @@
 msgstr ""
 "Project-Id-Version: git\n"
 "Report-Msgid-Bugs-To: Git Mailing List <git@vger.kernel.org>\n"
-"POT-Creation-Date: 2026-01-22 23:57+0000\n"
-"PO-Revision-Date: 2026-01-28 19:43+0100\n"
+"POT-Creation-Date: 2026-04-17 23:29+0800\n"
+"PO-Revision-Date: 2026-04-18 12:15+0800\n"
 "Last-Translator: Cédric Malard <c.malard-git@valdun.net>\n"
 "Language-Team: Jean-Noël Avila <jn.avila@free.fr>\n"
 "Language: fr\n"
@@ -99,10 +99,6 @@
 "X-Generator: Poedit 3.0.1\n"
 
 #, c-format
-msgid "%s cannot be negative"
-msgstr "%s doit être non négatif"
-
-#, c-format
 msgid "Huh (%s)?"
 msgstr "Hein (%s) ?"
 
@@ -259,20 +255,20 @@
 msgstr "Au revoir.\n"
 
 #, c-format
-msgid "Stage mode change [y,n,q,a,d%s,?]? "
-msgstr "Indexer le changement de mode [y,n,q,a,d%s,?] ? "
+msgid "Stage mode change%s [y,n,q,a,d%s,?]? "
+msgstr "Indexer le changement de mode%s [y,n,q,a,d%s,?] ? "
 
 #, c-format
-msgid "Stage deletion [y,n,q,a,d%s,?]? "
-msgstr "Indexer la suppression [y,n,q,a,d%s,?] ? "
+msgid "Stage deletion%s [y,n,q,a,d%s,?]? "
+msgstr "Indexer la suppression%s [y,n,q,a,d%s,?] ? "
 
 #, c-format
-msgid "Stage addition [y,n,q,a,d%s,?]? "
-msgstr "Indexer l'ajout [y,n,q,a,d%s,?] ? "
+msgid "Stage addition%s [y,n,q,a,d%s,?]? "
+msgstr "Indexer l'ajout%s [y,n,q,a,d%s,?] ? "
 
 #, c-format
-msgid "Stage this hunk [y,n,q,a,d%s,?]? "
-msgstr "Indexer cette section [y,n,q,a,d%s,?] ? "
+msgid "Stage this hunk%s [y,n,q,a,d%s,?]? "
+msgstr "Indexer cette section%s [y,n,q,a,d%s,?] ? "
 
 msgid ""
 "If the patch applies cleanly, the edited hunk will immediately be marked for "
@@ -295,20 +291,20 @@
 "d - ne pas indexer cette section ni les suivantes de ce fichier\n"
 
 #, c-format
-msgid "Stash mode change [y,n,q,a,d%s,?]? "
-msgstr "Remiser le changement de mode [y,n,q,a,d%s,?] ? "
+msgid "Stash mode change%s [y,n,q,a,d%s,?]? "
+msgstr "Remiser le changement de mode%s [y,n,q,a,d%s,?] ? "
 
 #, c-format
-msgid "Stash deletion [y,n,q,a,d%s,?]? "
-msgstr "Remiser la suppression [y,n,q,a,d%s,?] ? "
+msgid "Stash deletion%s [y,n,q,a,d%s,?]? "
+msgstr "Remiser la suppression%s [y,n,q,a,d%s,?] ? "
 
 #, c-format
-msgid "Stash addition [y,n,q,a,d%s,?]? "
-msgstr "Remiser l'ajout [y,n,q,a,d%s,?] ? "
+msgid "Stash addition%s [y,n,q,a,d%s,?]? "
+msgstr "Remiser l'ajout%s [y,n,q,a,d%s,?] ? "
 
 #, c-format
-msgid "Stash this hunk [y,n,q,a,d%s,?]? "
-msgstr "Remiser cette section [y,n,q,a,d%s,?] ? "
+msgid "Stash this hunk%s [y,n,q,a,d%s,?]? "
+msgstr "Remiser cette section%s [y,n,q,a,d%s,?] ? "
 
 msgid ""
 "If the patch applies cleanly, the edited hunk will immediately be marked for "
@@ -331,20 +327,20 @@
 "d - ne pas remiser cette section ni les suivantes de ce fichier\n"
 
 #, c-format
-msgid "Unstage mode change [y,n,q,a,d%s,?]? "
-msgstr "Désindexer le changement de mode [y,n,q,a,d%s,?] ? "
+msgid "Unstage mode change%s [y,n,q,a,d%s,?]? "
+msgstr "Désindexer le changement de mode%s [y,n,q,a,d%s,?] ? "
 
 #, c-format
-msgid "Unstage deletion [y,n,q,a,d%s,?]? "
-msgstr "Désindexer la suppression [y,n,q,a,d%s,?] ? "
+msgid "Unstage deletion%s [y,n,q,a,d%s,?]? "
+msgstr "Désindexer la suppression%s [y,n,q,a,d%s,?] ? "
 
 #, c-format
-msgid "Unstage addition [y,n,q,a,d%s,?]? "
-msgstr "Désindexer l'ajout [y,n,q,a,d%s,?] ? "
+msgid "Unstage addition%s [y,n,q,a,d%s,?]? "
+msgstr "Désindexer l'ajout%s [y,n,q,a,d%s,?] ? "
 
 #, c-format
-msgid "Unstage this hunk [y,n,q,a,d%s,?]? "
-msgstr "Désindexer cette section [y,n,q,a,d%s,?] ? "
+msgid "Unstage this hunk%s [y,n,q,a,d%s,?]? "
+msgstr "Désindexer cette section%s [y,n,q,a,d%s,?] ? "
 
 msgid ""
 "If the patch applies cleanly, the edited hunk will immediately be marked for "
@@ -367,20 +363,20 @@
 "d - ne pas désindexer cette section ni les suivantes de ce fichier\n"
 
 #, c-format
-msgid "Apply mode change to index [y,n,q,a,d%s,?]? "
-msgstr "Appliquer le changement de mode à l'index [y,n,q,a,d%s,?] ? "
+msgid "Apply mode change to index%s [y,n,q,a,d%s,?]? "
+msgstr "Appliquer le changement de mode à l'index%s [y,n,q,a,d%s,?] ? "
 
 #, c-format
-msgid "Apply deletion to index [y,n,q,a,d%s,?]? "
-msgstr "Appliquer la suppression à l'index [y,n,q,a,d%s,?] ? "
+msgid "Apply deletion to index%s [y,n,q,a,d%s,?]? "
+msgstr "Appliquer la suppression à l'index%s [y,n,q,a,d%s,?] ? "
 
 #, c-format
-msgid "Apply addition to index [y,n,q,a,d%s,?]? "
-msgstr "Appliquer l'ajout à l'index [y,n,q,a,d%s,?] ? "
+msgid "Apply addition to index%s [y,n,q,a,d%s,?]? "
+msgstr "Appliquer l'ajout à l'index%s [y,n,q,a,d%s,?] ? "
 
 #, c-format
-msgid "Apply this hunk to index [y,n,q,a,d%s,?]? "
-msgstr "Appliquer cette section à l'index [y,n,q,a,d%s,?] ? "
+msgid "Apply this hunk to index%s [y,n,q,a,d%s,?]? "
+msgstr "Appliquer cette section à l'index%s [y,n,q,a,d%s,?] ? "
 
 msgid ""
 "If the patch applies cleanly, the edited hunk will immediately be marked for "
@@ -403,20 +399,20 @@
 "d - ne pas appliquer cette section ni les suivantes de ce fichier\n"
 
 #, c-format
-msgid "Discard mode change from worktree [y,n,q,a,d%s,?]? "
-msgstr "Abandonner le changement de mode dans l'arbre [y,n,q,a,d%s,?] ? "
+msgid "Discard mode change from worktree%s [y,n,q,a,d%s,?]? "
+msgstr "Abandonner le changement de mode dans l'arbre%s [y,n,q,a,d%s,?] ? "
 
 #, c-format
-msgid "Discard deletion from worktree [y,n,q,a,d%s,?]? "
-msgstr "Abandonner la suppression dans l'arbre [y,n,q,a,d%s,?] ? "
+msgid "Discard deletion from worktree%s [y,n,q,a,d%s,?]? "
+msgstr "Abandonner la suppression dans l'arbre%s [y,n,q,a,d%s,?] ? "
 
 #, c-format
-msgid "Discard addition from worktree [y,n,q,a,d%s,?]? "
-msgstr "Abandonner l'ajout dans l'arbre [y,n,q,a,d%s,?] ? "
+msgid "Discard addition from worktree%s [y,n,q,a,d%s,?]? "
+msgstr "Abandonner l'ajout dans l'arbre%s [y,n,q,a,d%s,?] ? "
 
 #, c-format
-msgid "Discard this hunk from worktree [y,n,q,a,d%s,?]? "
-msgstr "Abandonner cette section dans l'arbre [y,n,q,a,d%s,?] ? "
+msgid "Discard this hunk from worktree%s [y,n,q,a,d%s,?]? "
+msgstr "Abandonner cette section dans l'arbre%s [y,n,q,a,d%s,?] ? "
 
 msgid ""
 "If the patch applies cleanly, the edited hunk will immediately be marked for "
@@ -439,22 +435,23 @@
 "d - ne pas supprimer cette section ni les suivantes de ce fichier\n"
 
 #, c-format
-msgid "Discard mode change from index and worktree [y,n,q,a,d%s,?]? "
+msgid "Discard mode change from index and worktree%s [y,n,q,a,d%s,?]? "
 msgstr ""
-"Abandonner le changement de mode dans l'index et l'arbre [y,n,q,a,d%s,?] ? "
+"Abandonner le changement de mode dans l'index et l'arbre%s [y,n,q,a,d%s,?] ? "
 
 #, c-format
-msgid "Discard deletion from index and worktree [y,n,q,a,d%s,?]? "
-msgstr "Abandonner la suppression de l'index et de l'arbre [y,n,q,a,d%s,?] ? "
-
-#, c-format
-msgid "Discard addition from index and worktree [y,n,q,a,d%s,?]? "
-msgstr "Abandonner l'ajout de l'index et de l'arbre [y,n,q,a,d%s,?] ? "
-
-#, c-format
-msgid "Discard this hunk from index and worktree [y,n,q,a,d%s,?]? "
+msgid "Discard deletion from index and worktree%s [y,n,q,a,d%s,?]? "
 msgstr ""
-"Supprimer la section dans l'index et l'arbre de travail [y,n,q,a,d%s,?] ? "
+"Abandonner la suppression de l'index et de l'arbre%s [y,n,q,a,d%s,?] ? "
+
+#, c-format
+msgid "Discard addition from index and worktree%s [y,n,q,a,d%s,?]? "
+msgstr "Abandonner l'ajout de l'index et de l'arbre%s [y,n,q,a,d%s,?] ? "
+
+#, c-format
+msgid "Discard this hunk from index and worktree%s [y,n,q,a,d%s,?]? "
+msgstr ""
+"Supprimer la section dans l'index et l'arbre de travail%s [y,n,q,a,d%s,?] ? "
 
 msgid ""
 "y - discard this hunk from index and worktree\n"
@@ -470,26 +467,26 @@
 "d - ne pas éliminer cette section ni les suivantes de ce fichier\n"
 
 #, c-format
-msgid "Apply mode change to index and worktree [y,n,q,a,d%s,?]? "
+msgid "Apply mode change to index and worktree%s [y,n,q,a,d%s,?]? "
 msgstr ""
-"Appliquer le changement de mode dans l'index et l'arbre de travail "
-"[y,n,q,a,d%s,?] ? "
+"Appliquer le changement de mode dans l'index et l'arbre de travail%s [y,n,q,"
+"a,d%s,?] ? "
 
 #, c-format
-msgid "Apply deletion to index and worktree [y,n,q,a,d%s,?]? "
+msgid "Apply deletion to index and worktree%s [y,n,q,a,d%s,?]? "
 msgstr ""
-"Appliquer la suppression dans l'index et l'arbre de travail "
-"[y,n,q,a,d%s,?] ? "
+"Appliquer la suppression dans l'index et l'arbre de travail%s [y,n,q,a,"
+"d%s,?] ? "
 
 #, c-format
-msgid "Apply addition to index and worktree [y,n,q,a,d%s,?]? "
+msgid "Apply addition to index and worktree%s [y,n,q,a,d%s,?]? "
 msgstr ""
-"Appliquer l'ajout dans l'index et l'arbre de travail [y,n,q,a,d%s,?] ? "
+"Appliquer l'ajout dans l'index et l'arbre de travail%s [y,n,q,a,d%s,?] ? "
 
 #, c-format
-msgid "Apply this hunk to index and worktree [y,n,q,a,d%s,?]? "
+msgid "Apply this hunk to index and worktree%s [y,n,q,a,d%s,?]? "
 msgstr ""
-"Appliquer la section à l'index et l'arbre de travail [y,n,q,a,d%s,?] ? "
+"Appliquer la section à l'index et l'arbre de travail%s [y,n,q,a,d%s,?] ? "
 
 msgid ""
 "y - apply this hunk to index and worktree\n"
@@ -505,21 +502,21 @@
 "d - ne pas appliquer cette section ni les suivantes de ce fichier\n"
 
 #, c-format
-msgid "Apply mode change to worktree [y,n,q,a,d%s,?]? "
+msgid "Apply mode change to worktree%s [y,n,q,a,d%s,?]? "
 msgstr ""
-"Appliquer le changement de mode dans l'arbre de travail [y,n,q,a,d%s,?] ? "
+"Appliquer le changement de mode dans l'arbre de travail%s [y,n,q,a,d%s,?] ? "
 
 #, c-format
-msgid "Apply deletion to worktree [y,n,q,a,d%s,?]? "
-msgstr "Appliquer la suppression dans l'arbre de travail [y,n,q,a,d%s,?] ? "
+msgid "Apply deletion to worktree%s [y,n,q,a,d%s,?]? "
+msgstr "Appliquer la suppression dans l'arbre de travail%s [y,n,q,a,d%s,?] ? "
 
 #, c-format
-msgid "Apply addition to worktree [y,n,q,a,d%s,?]? "
-msgstr "Appliquer l'ajout dans l'arbre de travail [y,n,q,a,d%s,?] ? "
+msgid "Apply addition to worktree%s [y,n,q,a,d%s,?]? "
+msgstr "Appliquer l'ajout dans l'arbre de travail%s [y,n,q,a,d%s,?] ? "
 
 #, c-format
-msgid "Apply this hunk to worktree [y,n,q,a,d%s,?]? "
-msgstr "Appliquer la section à l'arbre de travail [y,n,q,a,d%s,?] ? "
+msgid "Apply this hunk to worktree%s [y,n,q,a,d%s,?]? "
+msgstr "Appliquer la section à l'arbre de travail%s [y,n,q,a,d%s,?] ? "
 
 msgid ""
 "y - apply this hunk to worktree\n"
@@ -535,6 +532,10 @@
 "d - ne pas appliquer cette section ni les suivantes de ce fichier\n"
 
 #, c-format
+msgid "%s cannot be negative"
+msgstr "%s doit être non négatif"
+
+#, c-format
 msgid "could not parse hunk header '%.*s'"
 msgstr "impossible d'analyser l'entête de section '%.*s'"
 
@@ -630,6 +631,7 @@
 msgid "Nothing was applied.\n"
 msgstr "Rien n'a été appliqué.\n"
 
+#, c-format
 msgid ""
 "j - go to the next undecided hunk, roll over at the bottom\n"
 "J - go to the next hunk, roll over at the bottom\n"
@@ -641,7 +643,10 @@
 "e - manually edit the current hunk\n"
 "p - print the current hunk\n"
 "P - print the current hunk using the pager\n"
+"> - go to the next file, roll over at the bottom\n"
+"< - go to the previous file, roll over at the top\n"
 "? - print help\n"
+"HUNKS SUMMARY - Hunks: %d, USE: %d, SKIP: %d\n"
 msgstr ""
 "j - aller à section non-décidée suivante et reboucler au début si en bas\n"
 "J - aller à section suivante et reboucler au début si en bas\n"
@@ -653,12 +658,30 @@
 "e - éditer manuellement la section actuelle\n"
 "p - afficher la section actuelle\n"
 "P - afficher la section actuelle avec un paginateur\n"
+"> - aller au fichier prochain, reboucler au début si en bas\n"
+"< - aller au fichier précédent, reboucler à la fin si en haut\n"
 "? - afficher l'aide\n"
+"RÉSUMÉ DES SECTIONS : Sections : %d, UTILISÉES : %d, SAUTÉES : %d\n"
+
+msgid "'git apply' failed"
+msgstr "'git apply' a échoué"
+
+msgid " (was: y)"
+msgstr " (était : y)"
+
+msgid " (was: n)"
+msgstr " (était : n)"
 
 #, c-format
 msgid "Only one letter is expected, got '%s'"
 msgstr "une seule lettre est attendue, mais '%s' a été reçu"
 
+msgid "No next file"
+msgstr "Pas de fichier suivant"
+
+msgid "No previous file"
+msgstr "Pas de fichier précédent"
+
 msgid "No other hunk"
 msgstr "Aucune autre section"
 
@@ -711,9 +734,6 @@
 msgid "Unknown command '%s' (use '?' for help)"
 msgstr "commande inconnue : '%s' (utilisez '?' pour de l'aide)"
 
-msgid "'git apply' failed"
-msgstr "'git apply' a échoué"
-
 msgid "No changes."
 msgstr "Aucune modification."
 
@@ -721,6 +741,25 @@
 msgstr "Seuls des fichiers binaires ont changé."
 
 #, c-format
+msgid "Stage mode change [y,n,q,a,d%s,?]? "
+msgstr "Indexer le changement de mode [y,n,q,a,d%s,?] ? "
+
+#, c-format
+msgid "Stage deletion [y,n,q,a,d%s,?]? "
+msgstr "Indexer la suppression [y,n,q,a,d%s,?] ? "
+
+#, c-format
+msgid "Stage addition [y,n,q,a,d%s,?]? "
+msgstr "Indexer l'ajout [y,n,q,a,d%s,?] ? "
+
+#, c-format
+msgid "Stage this hunk [y,n,q,a,d%s,?]? "
+msgstr "Indexer cette section [y,n,q,a,d%s,?] ? "
+
+msgid "Revision does not refer to a commit"
+msgstr "La révision ne pointe pas sur un commit"
+
+#, c-format
 msgid ""
 "\n"
 "Disable this message with \"git config set advice.%s false\""
@@ -850,8 +889,8 @@
 "\n"
 "  git switch -\n"
 "\n"
-"Désactivez ce conseil en renseignant la variable de configuration "
-"advice.detachedHead à false\n"
+"Désactivez ce conseil en renseignant la variable de configuration advice."
+"detachedHead à false\n"
 "\n"
 
 #, c-format
@@ -913,14 +952,27 @@
 msgstr "regexec a retourné %d pour l'entrée : %s"
 
 #, c-format
-msgid "unable to find filename in patch at line %d"
-msgstr "nom de fichier de la rustine introuvable à la ligne %d"
+msgid "unable to find filename in patch at %s:%d"
+msgstr "nom de fichier introuvable dans la rustine à %s:%d"
+
+#, c-format
+msgid "git apply: bad git-diff - expected /dev/null, got %s at %s:%d"
+msgstr "git apply : mauvais git-diff - /dev/null attendu, %s trouvé à %s:%d"
 
 #, c-format
 msgid "git apply: bad git-diff - expected /dev/null, got %s on line %d"
 msgstr ""
-"git apply : mauvais format de git-diff - /dev/null attendu, %s trouvé à la "
-"ligne %d"
+"git apply : mauvais git-diff - /dev/null attendu, %s trouvé à la ligne %d"
+
+#, c-format
+msgid "git apply: bad git-diff - inconsistent new filename at %s:%d"
+msgstr ""
+"git apply : mauvais git-diff - nouveau nom de fichier incohérent à %s:%d"
+
+#, c-format
+msgid "git apply: bad git-diff - inconsistent old filename at %s:%d"
+msgstr ""
+"git apply : mauvais git-diff - ancien nom de fichier incohérent à %s:%d"
 
 #, c-format
 msgid "git apply: bad git-diff - inconsistent new filename on line %d"
@@ -935,11 +987,19 @@
 "la ligne %d"
 
 #, c-format
+msgid "git apply: bad git-diff - expected /dev/null at %s:%d"
+msgstr "git apply : mauvais git-diff - /dev/null attendu à %s:%d"
+
+#, c-format
 msgid "git apply: bad git-diff - expected /dev/null on line %d"
 msgstr ""
 "git apply : mauvais format de git-diff - /dev/null attendu à la ligne %d"
 
 #, c-format
+msgid "invalid mode at %s:%d: %s"
+msgstr "mode invalide dans %s:%d : %s"
+
+#, c-format
 msgid "invalid mode on line %d: %s"
 msgstr "mode invalide dans la ligne %d : %s"
 
@@ -950,6 +1010,20 @@
 #, c-format
 msgid ""
 "git diff header lacks filename information when removing %d leading pathname "
+"component at %s:%d"
+msgid_plural ""
+"git diff header lacks filename information when removing %d leading pathname "
+"components at %s:%d"
+msgstr[0] ""
+"information de nom de fichier manquante dans l'en-tête de git diff lors de "
+"la suppression de %d composant de préfixe de chemin à %s:%d"
+msgstr[1] ""
+"information de nom de fichier manquante dans l'en-tête de git diff lors de "
+"la suppression de %d composants de préfixe de chemin à %s:%d"
+
+#, c-format
+msgid ""
+"git diff header lacks filename information when removing %d leading pathname "
 "component (line %d)"
 msgid_plural ""
 "git diff header lacks filename information when removing %d leading pathname "
@@ -962,6 +1036,11 @@
 "la suppression de %d composants de préfixe de chemin (ligne %d)"
 
 #, c-format
+msgid "git diff header lacks filename information at %s:%d"
+msgstr ""
+"information de nom de fichier manquante dans l'en-tête de git diff à %s:%d"
+
+#, c-format
 msgid "git diff header lacks filename information (line %d)"
 msgstr ""
 "information de nom de fichier manquante dans l'en-tête de git diff (ligne %d)"
@@ -971,8 +1050,8 @@
 msgstr "recomptage : ligne inattendue : %.*s"
 
 #, c-format
-msgid "patch fragment without header at line %d: %.*s"
-msgstr "fragment de rustine sans en-tête à la ligne %d : %.*s"
+msgid "patch fragment without header at %s:%d: %.*s"
+msgstr "fragment de rustine sans en-tête à %s:%d : %.*s"
 
 msgid "new file depends on old contents"
 msgstr "le nouveau fichier dépend de contenus anciens"
@@ -981,8 +1060,8 @@
 msgstr "le fichier supprimé a encore du contenu"
 
 #, c-format
-msgid "corrupt patch at line %d"
-msgstr "rustine corrompue à la ligne %d"
+msgid "corrupt patch at %s:%d"
+msgstr "rustine corrompue à %s:%d"
 
 #, c-format
 msgid "new file %s depends on old contents"
@@ -997,16 +1076,16 @@
 msgstr "** attention : le fichier %s devient vide mais n'est pas supprimé"
 
 #, c-format
-msgid "corrupt binary patch at line %d: %.*s"
-msgstr "rustine binaire corrompue à la ligne %d : %.*s"
+msgid "corrupt binary patch at %s:%d: %.*s"
+msgstr "rustine binaire corrompue à %s:%d : %.*s"
 
 #, c-format
-msgid "unrecognized binary patch at line %d"
-msgstr "rustine binaire non reconnue à la ligne %d"
+msgid "unrecognized binary patch at %s:%d"
+msgstr "rustine binaire non reconnue à %s:%d"
 
 #, c-format
-msgid "patch with only garbage at line %d"
-msgstr "rustine totalement incompréhensible à la ligne %d"
+msgid "patch with only garbage at %s:%d"
+msgstr "rustine totalement incompréhensible à %s:%d"
 
 #, c-format
 msgid "unable to read symlink %s"
@@ -1274,6 +1353,14 @@
 msgstr "lecture du fichier d'index impossible"
 
 #, c-format
+msgid "option -p expects a non-negative integer, got '%s'"
+msgstr "l'option -p attend un entier positif ou nul, et a obtenu '%s'"
+
+#, c-format
+msgid "unable to normalize directory: '%s'"
+msgstr "impossible de normaliser le répertoire : '%s'"
+
+#, c-format
 msgid "can't open patch '%s': %s"
 msgstr "ouverture impossible de la rustine '%s' :%s"
 
@@ -1968,6 +2055,11 @@
 msgid "select hunks interactively"
 msgstr "sélection interactive des sections"
 
+msgid "auto advance to the next file when selecting hunks interactively"
+msgstr ""
+"avancer automatiquement au prochain fichier lors de la sélection interactive "
+"des sections"
+
 msgid "edit current diff and apply"
 msgstr "édition du diff actuel et application"
 
@@ -2474,6 +2566,10 @@
 msgid "Restrict the missing objects to the current sparse-checkout"
 msgstr "Restreindre les objets manquants à l'extraction clairsemée actuelle"
 
+#, c-format
+msgid "unrecognized argument: %s"
+msgstr "argument non reconnu : %s"
+
 msgid ""
 "git bisect start [--term-(bad|new)=<term-new> --term-(good|old)=<term-old>]\n"
 "                 [--no-checkout] [--first-parent] [<bad> [<good>...]] [--] "
@@ -3183,11 +3279,11 @@
 msgstr "HEAD non trouvée sous refs/heads !"
 
 msgid ""
-"branch with --recurse-submodules can only be used if "
-"submodule.propagateBranches is enabled"
+"branch with --recurse-submodules can only be used if submodule."
+"propagateBranches is enabled"
 msgstr ""
-"brancher avec --recurse-submodules ne peut être utilisé que si "
-"submodule.propagateBranches est activé"
+"brancher avec --recurse-submodules ne peut être utilisé que si submodule."
+"propagateBranches est activé"
 
 msgid "--recurse-submodules can only be used to create branches"
 msgstr "--recurse-submodules ne peut être utilisé que pour créer des branches"
@@ -3691,18 +3787,6 @@
 msgid "copy out the files from named stage"
 msgstr "copier les fichiers depuis l'index nommé"
 
-msgid "git checkout [<options>] <branch>"
-msgstr "git checkout [<options>] <branche>"
-
-msgid "git checkout [<options>] [<branch>] -- <file>..."
-msgstr "git checkout [<options>] [<branche>] -- <fichier>..."
-
-msgid "git switch [<options>] [<branch>]"
-msgstr "git switch [<options>] <branche>"
-
-msgid "git restore [<options>] [--source=<branch>] <file>..."
-msgstr "git restore [<options>] [--source=<branche>] <fichier>..."
-
 #, c-format
 msgid "path '%s' does not have our version"
 msgstr "le chemin '%s' n'a pas notre version"
@@ -3894,11 +3978,12 @@
 "'%s' pourrait être un fichier local ou un branche de suivi.\n"
 "Veuillez utiliser -- (et --no-guess en facultatif) pour les distinguer"
 
+#, c-format
 msgid ""
 "If you meant to check out a remote tracking branch on, e.g. 'origin',\n"
 "you can do so by fully qualifying the name with the --track option:\n"
 "\n"
-"    git checkout --track origin/<name>\n"
+"    git %s --track origin/<name>\n"
 "\n"
 "If you'd like to always have checkouts of an ambiguous <name> prefer\n"
 "one remote, e.g. the 'origin' remote, consider setting\n"
@@ -3907,11 +3992,11 @@
 "Si vous souhaitiez extraire une branche de suivi distant sur 'origin',\n"
 "par exemple, qualifiez-la complètement avec l'option --track :\n"
 "\n"
-"    git checkout --track origin/<nom>\n"
+"    git %s --track origin/<nom>\n"
 "\n"
-"Si vous souhaitez privilégier un distant particulier lorsque <nom> est\n"
-"ambigu, vous pouvez positionner checkout.defaultRemote=origin dans\n"
-"votre config."
+"Si vous souhaitez privilégier un distant particulier, par ex. 'origin'\n"
+"lorsque <nom> est ambigu, vous pouvez positionner\n"
+"checkout.defaultRemote=origin dans votre config."
 
 #, c-format
 msgid "'%s' matched multiple (%d) remote tracking branches"
@@ -4058,6 +4143,18 @@
 msgid "do not limit pathspecs to sparse entries only"
 msgstr "ne pas limiter les spécificateurs de chemins aux seuls éléments creux"
 
+msgid "git checkout [<options>] <branch>"
+msgstr "git checkout [<options>] <branche>"
+
+msgid "git checkout [<options>] [<branch>] -- <file>..."
+msgstr "git checkout [<options>] [<branche>] -- <fichier>..."
+
+msgid "git switch [<options>] [<branch>]"
+msgstr "git switch [<options>] <branche>"
+
+msgid "git restore [<options>] [--source=<branch>] <file>..."
+msgstr "git restore [<options>] [--source=<branche>] <fichier>..."
+
 #, c-format
 msgid "options '-%c', '-%c', and '%s' cannot be used together"
 msgstr ""
@@ -5880,10 +5977,6 @@
 msgid "git diff-pairs -z [<diff-options>]"
 msgstr "git diff-pairs -z [<options-de-diff>]"
 
-#, c-format
-msgid "unrecognized argument: %s"
-msgstr "argument non reconnu : %s"
-
 msgid "working without -z is not supported"
 msgstr "l'invocation sans -z n'est pas supportée"
 
@@ -6103,13 +6196,6 @@
 msgstr ""
 "commit signé %s rencontré ; utilisez --signed-commits=<mode> pour le gérer"
 
-msgid ""
-"'strip-if-invalid' is not a valid mode for git fast-export with --signed-"
-"commits=<mode>"
-msgstr ""
-"'strip-if-invalid' n'est pas un mode valide pour git fast-export avec --"
-"signed-commits=<mode>"
-
 #, c-format
 msgid ""
 "omitting tag %s,\n"
@@ -6136,13 +6222,6 @@
 msgstr ""
 "étiquette signée %s rencontrée ; utilisez --signed-tags=<mode> pour la gérer"
 
-msgid ""
-"'strip-if-invalid' is not a valid mode for git fast-export with --signed-"
-"tags=<mode>"
-msgstr ""
-"'strip-if-invalid' n'est pas un mode valide pour git fast-export avec --"
-"signed-tags=<mode>"
-
 #, c-format
 msgid ""
 "tag %s tags unexported object; use --tag-of-filtered-object=<mode> to handle "
@@ -6565,6 +6644,39 @@
 "suppression de la signature invalide pour le commit\n"
 "  prétendument par %s"
 
+#, c-format
+msgid ""
+"replacing invalid signature for commit '%.100s...'\n"
+"  allegedly by %s"
+msgstr ""
+"remplacement de la signature invalide pour le commit '%.100s...'\n"
+"  prétendument par %s"
+
+#, c-format
+msgid ""
+"replacing invalid signature for commit '%.*s'\n"
+"  allegedly by %s"
+msgstr ""
+"remplacement de la signature invalide pour le commit '%.*s'\n"
+"  prétendument par %s"
+
+#, c-format
+msgid ""
+"replacing invalid signature for commit\n"
+"  allegedly by %s"
+msgstr ""
+"remplacement de la signature invalide pour le commit\n"
+"  prétendument par %s"
+
+msgid "aborting due to invalid signature"
+msgstr "abandon pour cause de signature invalide"
+
+msgid "signing commits in interoperability mode is unsupported"
+msgstr "signer les commits en mode d'interopérabilité n'est pas pris en charge"
+
+msgid "failed to sign commit object"
+msgstr "échec de la signature de l'objet commit"
+
 msgid "expected committer but didn't get one"
 msgstr "validateur attendu mais aucune information fournie"
 
@@ -6578,6 +6690,9 @@
 msgid "importing a commit signature verbatim"
 msgstr "import d'une signature de commit verbatim"
 
+msgid "failed to sign tag object"
+msgstr "échec de la signature de l'objet étiquette"
+
 #, c-format
 msgid "importing a tag signature verbatim for tag '%s'"
 msgstr "import d'une signature d'étiquette verbatim pour l'étiquette '%s'"
@@ -6590,13 +6705,6 @@
 msgstr ""
 "étiquette signée rencontrée ; utilisez --signed-tags=<mode> pour la gérer"
 
-msgid ""
-"'strip-if-invalid' is not a valid mode for git fast-import with --signed-"
-"tags=<mode>"
-msgstr ""
-"'strip-if-invalid' n'est pas un mode valide pour git fast-import avec --"
-"signed-tags=<mode>"
-
 #, c-format
 msgid "expected 'from' command, got '%s'"
 msgstr "Commande 'from' attendue, '%s' trouvé"
@@ -7128,8 +7236,8 @@
 msgstr "Le protocole ne prend pas en charge --negotiate-only, abandon"
 
 msgid ""
-"--filter can only be used with the remote configured in "
-"extensions.partialclone"
+"--filter can only be used with the remote configured in extensions."
+"partialclone"
 msgstr ""
 "--filter ne peut être utilisé qu'avec le dépôt distant configuré dans "
 "extensions.partialclone"
@@ -8191,12 +8299,161 @@
 msgid "'git help config' for more information"
 msgstr "'git help config' pour plus d'information"
 
-msgid ""
-"git hook run [--ignore-missing] [--to-stdin=<path>] <hook-name> [-- <hook-"
-"args>]"
+msgid "git history reword <commit> [--dry-run] [--update-refs=(branches|head)]"
 msgstr ""
-"git hook run [--ignore-missing] [--to-stdin=<chemin>] <nom-de-crochet> [-- "
-"<arguments-de-crochet>]"
+"git history reword <commit> [--dry-run] [--update-refs=(branches|head)]"
+
+msgid ""
+"git history split <commit> [--dry-run] [--update-refs=(branches|head)] [--] "
+"[<pathspec>...]"
+msgstr ""
+"git history split <commit> [--dry-run] [--update-refs=(branches|head)] [--] "
+"[<spéc-de-chemin>...]"
+
+#, c-format
+msgid ""
+"Please enter the commit message for the %s changes. Lines starting\n"
+"with '%s' will be ignored, and an empty message aborts the commit.\n"
+msgstr ""
+"Veuillez saisir le message de validation pour les modifications %s. Les "
+"lignes\n"
+"commençant par '%s' seront ignorées, et un message vide abandonne la "
+"validation.\n"
+
+#, c-format
+msgid "Aborting commit as launching the editor failed.\n"
+msgstr "Abandon de la validation dû à l'échec du lancement de l'éditeur\n"
+
+#, c-format
+msgid "unable to parse parent commit %s"
+msgstr "impossible d'analyser le commit parent %s"
+
+#, c-format
+msgid "%s expects one of 'branches' or 'head'"
+msgstr "'branches' ou 'head' attendu par %s"
+
+msgid "error preparing revisions"
+msgstr "erreur lors de la préparation des révisions"
+
+msgid "replaying merge commits is not supported yet!"
+msgstr "rejouer des commits de fusion n'est pas encore géré !"
+
+msgid "cannot look up HEAD"
+msgstr "impossible de rechercher HEAD"
+
+msgid "cannot determine descendance"
+msgstr "impossible de déterminer la descendance"
+
+msgid ""
+"rewritten commit must be an ancestor of HEAD when using --update-refs=head"
+msgstr ""
+"le commit ré-écrit doit être un ancêtre de HEAD quand --update-refs=head est "
+"utilisé"
+
+#, c-format
+msgid "failed to begin ref transaction: %s"
+msgstr "impossible de commencer la transaction de réf ; %s"
+
+#, c-format
+msgid "failed to update ref '%s': %s"
+msgstr "échec de la mise à jour de la réf '%s' : %s"
+
+#, c-format
+msgid "failed to commit ref transaction: %s"
+msgstr "impossible de valider la transaction de réf : %s"
+
+msgid "control which refs should be updated"
+msgstr "contrôler quelle réf devrait être mise à jour"
+
+msgid "perform a dry-run without updating any refs"
+msgstr "réaliser un essai à blanc sans mettre à jour les réfs"
+
+msgid "command expects a single revision"
+msgstr "une seule révision attendue par la commande"
+
+#, c-format
+msgid "commit cannot be found: %s"
+msgstr "commit non trouvé : %s"
+
+msgid "failed writing reworded commit"
+msgstr "échec de l'écriture du commit reformulé"
+
+msgid "failed replaying descendants"
+msgstr "échec du rejeu des descendants"
+
+msgid "unable to populate index with tree"
+msgstr "impossible de peupler l'index avec l'arbre"
+
+msgid "unable to acquire index lock"
+msgstr "impossible d'acquérir le verrou de l'index"
+
+msgid "failed reading temporary index"
+msgstr "impossible de lire l'index temporaire"
+
+msgid "failed split tree"
+msgstr "échec de scission de l'arbre"
+
+msgid "split commit is empty"
+msgstr "le commit scindé est vide"
+
+msgid "split commit tree matches original commit"
+msgstr "l'arbre du commit scindé correspond au commit original"
+
+msgid "failed writing first commit"
+msgstr "échec de l'écriture du premier commit"
+
+msgid "failed writing second commit"
+msgstr "échec de l'écriture du second commit"
+
+msgid "control ref update behavior"
+msgstr "contrôler le comportement de mise à jour de réf"
+
+msgid "command expects a committish"
+msgstr "la commande attend un commit-esque"
+
+msgid "cannot split up merge commit"
+msgstr "impossible de scinder un commit de fusion"
+
+msgid ""
+"git hook run [--allow-unknown-hook-name] [--ignore-missing] [--to-"
+"stdin=<path>] <hook-name> [-- <hook-args>]"
+msgstr ""
+"git hook run [--allow-unknown-hook-name] [--ignore-missing] [--to-"
+"stdin=<chemin>] <nom-de-crochet> [-- <arguments-de-crochet>]"
+
+msgid ""
+"git hook list [--allow-unknown-hook-name] [-z] [--show-scope] <hook-name>"
+msgstr ""
+"git hook list [--allow-unknown-hook-name] [-z] [--show-scope] <nom-de-"
+"crochet>"
+
+msgid "use NUL as line terminator"
+msgstr "la ligne se termine par NUL"
+
+msgid "show the config scope that defined each hook"
+msgstr "afficher la portée de configuration qui a défini chaque crochet"
+
+msgid "allow running a hook with a non-native hook name"
+msgstr "pemettre de lancer un crochet avec un nom de crochet non natif"
+
+msgid "you must specify a hook event name to list"
+msgstr "vous devez spécifier un nom d'événement de crochet à lister"
+
+#, c-format
+msgid ""
+"unknown hook event '%s';\n"
+"use --allow-unknown-hook-name to allow non-native hook names"
+msgstr ""
+"événement de crochet inconnu '%s' ;\n"
+"utilisez --allow-unknown-hook-name pour permettre les noms de crochet non "
+"natifs"
+
+#, c-format
+msgid "no hooks found for event '%s'"
+msgstr "pas de crochet trouvé pour l'événement '%s'"
+
+msgid "hook from hookdir"
+msgstr "crochet depuis hookdir"
 
 msgid "silently ignore missing requested <hook-name>"
 msgstr "ignorer silencieusement le <nom-de-crochet> requis manquant"
@@ -8511,21 +8768,6 @@
 "                       [--parse] [<fichier>...]"
 
 #, c-format
-msgid "could not stat %s"
-msgstr "stat impossible de %s"
-
-#, c-format
-msgid "file %s is not a regular file"
-msgstr "%s n'est pas un fichier régulier"
-
-#, c-format
-msgid "file %s is not writable by user"
-msgstr "le fichier %s n'est pas inscriptible par l'utilisateur"
-
-msgid "could not open temporary file"
-msgstr "impossible de créer un fichier temporaire"
-
-#, c-format
 msgid "could not read input file '%s'"
 msgstr "impossible de lire le fichier d'entrée '%s'"
 
@@ -8533,6 +8775,10 @@
 msgstr "impossible de lire depuis l'entrée standard"
 
 #, c-format
+msgid "could not write to temporary file '%s'"
+msgstr "impossible d'écrire le fichier temporaire '%s'"
+
+#, c-format
 msgid "could not rename temporary file to %s"
 msgstr "impossible de renommer un fichier temporaire en %s"
 
@@ -8557,8 +8803,8 @@
 msgid "output only the trailers"
 msgstr "éliminer les lignes terminales vides"
 
-msgid "do not apply trailer.* configuration variables"
-msgstr "ne pas appliquer les variables de configuration trailer.*"
+msgid "do not apply trailer.<key-alias> configuration variables"
+msgstr "ne pas appliquer les variables de configuration trailer.<alias-de-clé>"
 
 msgid "reformat multiline trailer values as single-line values"
 msgstr ""
@@ -8580,22 +8826,23 @@
 msgid "no input file given for in-place editing"
 msgstr "aucun fichier en entrée pour l'éditon sur place"
 
-msgid "last-modified can only operate on one tree at a time"
-msgstr "last-modified ne peut opérer que sur un arbre à la fois"
+msgid "last-modified can only operate on one commit at a time"
+msgstr "last-modified ne peut opérer que sur un commit à la fois"
+
+#, c-format
+msgid "revision argument '%s' is a %s, not a commit-ish"
+msgstr "l'argument de révision '%s' est un %s, pas un commit-esque"
 
 #, c-format
 msgid "unknown last-modified argument: %s"
 msgstr "argument de last-modified inconnu : %s"
 
-msgid "unable to setup last-modified"
-msgstr "impossible de renseigner last-modified"
-
 msgid ""
-"git last-modified [--recursive] [--show-trees] [<revision-range>] [[--] "
-"<path>...]"
+"git last-modified [--recursive] [--show-trees] [--max-depth=<depth>] [-z]\n"
+"                  [<revision-range>] [[--] <pathspec>...]"
 msgstr ""
-"git last-modified [--recursive] [--show-trees] [<plage-de-révisions>] [[--] "
-"<chemin>...]"
+"git last-modified [--recursive] [--show-trees] [--max-depth=<depth>] [-z]\n"
+"                  [<plage-de-révisions>] [[--] <spéc-de-chemin>...]"
 
 msgid "recurse into subtrees"
 msgstr "parcourir les sous-arbres"
@@ -8603,6 +8850,12 @@
 msgid "show tree entries when recursing into subtrees"
 msgstr "afficher les entrées lors de la récurssion dans les sous-arbres"
 
+msgid "maximum tree depth to recurse"
+msgstr "profondeur d'arbre maximum de récursion"
+
+msgid "lines are separated with NUL character"
+msgstr "les lignes sont séparées par un caractère NUL"
+
 msgid "git log [<options>] [<revision-range>] [[--] <path>...]"
 msgstr "git log [<options>] [<plage de révisions>] [[--] <chemin>...]"
 
@@ -8677,6 +8930,18 @@
 msgstr "format.headers sans valeur"
 
 #, c-format
+msgid "bad boolean config value '%s' for '%s'"
+msgstr "valeur booléenne de configuration invalide '%s' pour '%s'"
+
+#, c-format
+msgid ""
+"'%s' used to accept any value and treat that as 'true'.\n"
+"Now it only accepts boolean values, like what '%s' does.\n"
+msgstr ""
+"'%s' acceptait n'importe quelle valeur et la traitait comm 'true'.\n"
+"À présent, elle n'accepte que les valeurs booléennes, comme '%s' le fait.\n"
+
+#, c-format
 msgid "cannot open patch file %s"
 msgstr "impossible d'ouvrir le fichier correctif %s"
 
@@ -8697,6 +8962,10 @@
 msgstr "échec de création du fichier de lettre de motivation"
 
 #, c-format
+msgid "'%s' is not a valid format string"
+msgstr "'%s' n'est pas une chaîne de format valide"
+
+#, c-format
 msgid "insane in-reply-to: %s"
 msgstr "in-reply-to aberrant : %s"
 
@@ -8763,6 +9032,14 @@
 msgid "generate a cover letter"
 msgstr "générer une lettre de motivation"
 
+msgid "format-spec"
+msgstr "spéc-de-format"
+
+msgid "format spec used for the commit list in the cover letter"
+msgstr ""
+"le spéc de format utilisé pour la liste de commits dans la lettre de "
+"motivation"
+
 msgid "use simple number sequence for output file names"
 msgstr ""
 "utiliser une séquence simple de nombres pour les nom des fichiers de sortie"
@@ -9659,11 +9936,20 @@
 msgstr "autoriser la création de plus d'un arbre"
 
 msgid ""
-"git multi-pack-index [<options>] write [--preferred-pack=<pack>][--refs-"
-"snapshot=<path>]"
+"git multi-pack-index [<options>] write [--preferred-pack=<pack>]\n"
+"  [--[no-]bitmap] [--[no-]incremental] [--[no-]stdin-packs]\n"
+"  [--refs-snapshot=<path>]"
 msgstr ""
-"git multi-pack-index [<options>] write [--preferred-pack=<paquet>][--refs-"
-"snapshot=<chemin>]"
+"git multi-pack-index [<options>] write [--preferred-pack=<paquet>]\n"
+"  [--[no-]bitmap] [--[no-]incremental] [--[no-]stdin-packs]\n"
+"  [--refs-snapshot=<chemin>]"
+
+msgid ""
+"git multi-pack-index [<options>] compact [--[no-]incremental]\n"
+"  [--[no-]bitmap] <from> <to>"
+msgstr ""
+"git multi-pack-index [<options>] compact [--[no-]incremental]\n"
+"  [--[no-]bitmap] <depuis> <vers>"
 
 msgid "git multi-pack-index [<options>] verify"
 msgstr "git multi-pack-index [<options>] verify"
@@ -9700,6 +9986,17 @@
 msgid "refs snapshot for selecting bitmap commits"
 msgstr "instantané des réfs pour sélectionner les commits de bitmap"
 
+#, c-format
+msgid "could not find MIDX: %s"
+msgstr "impossible de trouver le MIDX : %s"
+
+msgid "MIDX compaction endpoints must be unique"
+msgstr "les points de terminaison de compaction de MIDX doivent être uniques"
+
+#, c-format
+msgid "MIDX %s must be an ancestor of %s"
+msgstr "le MIDX %s doit être l'ancêtre de %s"
+
 msgid ""
 "during repack, collect pack-files of smaller size into a batch that is "
 "larger than this size"
@@ -10324,16 +10621,16 @@
 msgstr "impossible d'obtenir le type de l'objet %s dans le paquet %s"
 
 #, c-format
+msgid "could not find pack '%s'"
+msgstr "impossible de trouver le paquet '%s'"
+
+#, c-format
 msgid "packfile %s is a promisor but --exclude-promisor-objects was given"
 msgstr ""
 "le fichier paquet %s est un prometteur mais --exclude-promisor-objects a été "
 "spécifé"
 
 #, c-format
-msgid "could not find pack '%s'"
-msgstr "impossible de trouver le paquet '%s'"
-
-#, c-format
 msgid "packfile %s cannot be accessed"
 msgstr "le fichier paquet %s ne peut être accédé"
 
@@ -10362,9 +10659,6 @@
 "ID d'objet attendu, reçu des données illisibles :\n"
 "%s"
 
-msgid "could not load cruft pack .mtimes"
-msgstr "impossible de charger le paquet déchet des mtimes"
-
 msgid "cannot open pack index"
 msgstr "impossible d'ouvrir l'index de paquet"
 
@@ -10917,8 +11211,8 @@
 msgid ""
 "You didn't specify any refspecs to push, and push.default is \"nothing\"."
 msgstr ""
-"Vous n'avez pas spécifié de spécifications de référence à pousser, et "
-"push.default est \"nothing\"."
+"Vous n'avez pas spécifié de spécifications de référence à pousser, et push."
+"default est \"nothing\"."
 
 #, c-format
 msgid ""
@@ -12660,52 +12954,40 @@
 msgstr "-l n'accepte qu'un motifs"
 
 #, c-format
-msgid "'%s' is not a valid commit-ish for %s"
-msgstr "'%s' n'est pas un commit-esque valide pour l'option %s"
-
-msgid "need some commits to replay"
-msgstr "commits requis pour pouvoir rejouer"
-
-msgid "all positive revisions given must be references"
-msgstr "toutes les révisions positives fournies doivent être des références"
-
-msgid "argument to --advance must be a reference"
-msgstr "l'argument de --advance doit être une référence"
-
-msgid ""
-"cannot advance target with multiple sources because ordering would be ill-"
-"defined"
-msgstr ""
-"impossible d'avancer la cible avec des sources multiples parce l'ordre ne "
-"serait pas total"
-
-#, c-format
 msgid "invalid %s value: '%s'"
 msgstr "valeur invalide de %s : '%s'"
 
 msgid ""
-"(EXPERIMENTAL!) git replay ([--contained] --onto <newbase> | --advance "
-"<branch>) [--ref-action[=<mode>]] <revision-range>"
+"(EXPERIMENTAL!) git replay ([--contained] --onto=<newbase> | --"
+"advance=<branch> | --revert=<branch>)\n"
+"[--ref=<ref>] [--ref-action=<mode>] <revision-range>"
 msgstr ""
-"(EXPERIMENTAL!) git replay ([--contained] --onto <nouvelle-base> | --advance "
-"<branche>) [--ref-action[=<mode>]] <plage-de-révision>"
-
-msgid "make replay advance given branch"
-msgstr "faire rejouer en avançant la branche indiquée"
-
-msgid "replay onto given commit"
-msgstr "rejouer par-dessus le commit indiqué"
+"(EXPERIMENTAL!) git replay ([--contained] --onto=<nouvelle-base> | --"
+"advance=<branche> | --revert=<branche>)\n"
+"[--ref=<réf>] [--ref-action=<mode>] <plage-de-révision>"
 
 msgid "update all branches that point at commits in <revision-range>"
 msgstr ""
 "mettre à jour les branches qui pointent sur les commits dans <plage-de-"
 "révisions>"
 
+msgid "replay onto given commit"
+msgstr "rejouer par-dessus le commit indiqué"
+
+msgid "make replay advance given branch"
+msgstr "faire rejouer en avançant la branche indiquée"
+
+msgid "revert commits onto given branch"
+msgstr "inverser les commits sur la branche fournie"
+
+msgid "reference to update with result"
+msgstr "la référence à mettre à jour avec le résultat"
+
 msgid "control ref update behavior (update|print)"
 msgstr "contrôler le comportement de mise à jour de ref (update|print)"
 
-msgid "option --onto or --advance is mandatory"
-msgstr "une option --onto ou --advance est obligatoire"
+msgid "exactly one of --onto, --advance, or --revert is required"
+msgstr "un seul des paramètres --onto, --advance ou --revert  est requis"
 
 #, c-format
 msgid ""
@@ -12716,30 +12998,12 @@
 "dans 'struct rev_info' sera forcé"
 
 #, c-format
-msgid "failed to begin ref transaction: %s"
-msgstr "impossible de commencer la transaction de réf ; %s"
-
-msgid "error preparing revisions"
-msgstr "erreur lors de la préparation des révisions"
-
-msgid "replaying down from root commit is not supported yet!"
-msgstr "rejouer depuis le commit racine n'est pas encore géré !"
-
-msgid "replaying merge commits is not supported yet!"
-msgstr "rejouer des commits de fusion n'est pas encore géré !"
-
-#, c-format
-msgid "failed to update ref '%s': %s"
-msgstr "échec de la mise à jour de la réf '%s' : %s"
-
-#, c-format
-msgid "failed to commit ref transaction: %s"
-msgstr "impossible de valider la transaction de réf : %s"
-
-#, c-format
 msgid "key '%s' not found"
 msgstr "clé '%s' non trouvée"
 
+msgid "--keys can only be used with --format=lines or --format=nul"
+msgstr "--keys ne peut être utilisé qu'avec --format=lines ou --format=nul"
+
 #, c-format
 msgid "invalid format '%s'"
 msgstr "format invalide '%s'"
@@ -12753,6 +13017,12 @@
 msgid "print all keys/values"
 msgstr "afficher toutes les clés/valeurs"
 
+msgid "show keys"
+msgstr "afficher les clé"
+
+msgid "--keys cannot be used with a <key> or --all"
+msgstr "--keys ne peut être utilisé ni avec une <clé> ou ni avec --all"
+
 msgid "unsupported output format"
 msgstr "format de sortie non géré"
 
@@ -12795,6 +13065,18 @@
 msgid "Disk size"
 msgstr "Taille sur disque"
 
+msgid "Largest objects"
+msgstr "Objets les plus gros"
+
+msgid "Maximum size"
+msgstr "Taille maximale"
+
+msgid "Maximum parents"
+msgstr "Nombre maximum de parents"
+
+msgid "Maximum entries"
+msgstr "Nombre maximum d'entrées"
+
 msgid "Repository structure"
 msgstr "Structure de dépôt"
 
@@ -13379,6 +13661,44 @@
 msgid "Unknown hash algorithm"
 msgstr "Algorithme d'empreinte inconnu"
 
+msgid "assuming SHA-1; use --object-format to override"
+msgstr "SHA-1 supposé ; utilisez --object-format pour surcharger"
+
+msgid "unable to read header"
+msgstr "impossible d'analyser l'entête"
+
+msgid "unknown index version"
+msgstr "version d'index inconnue"
+
+msgid "unable to read index"
+msgstr "lecture d'index impossible"
+
+msgid "corrupt index file"
+msgstr "fichier d'index corrompu"
+
+#, c-format
+msgid "unable to read entry %u/%u"
+msgstr "impossible de lire l'entrée %u/%u"
+
+#, c-format
+msgid "unable to read sha1 %u/%u"
+msgstr "impossible de lire le sha1 %u/%u"
+
+#, c-format
+msgid "unable to read crc %u/%u"
+msgstr "impossible de lire le crc %u/%u"
+
+#, c-format
+msgid "unable to read 32b offset %u/%u"
+msgstr "impossible de lire le décalage 32b %u/%u"
+
+msgid "inconsistent 64b offset index"
+msgstr "index de décalage 64b incohérent"
+
+#, c-format
+msgid "unable to read 64b offset %u"
+msgstr "impossible de lire le décalage 64b %u"
+
 msgid ""
 "git show-ref [--head] [-d | --dereference]\n"
 "             [-s | --hash[=<n>]] [--abbrev[=<n>]] [--branches] [--tags]\n"
@@ -13642,19 +13962,19 @@
 msgstr "git stash store [(-m | --message) <message>] [-q | --quiet] <remise>"
 
 msgid ""
-"git stash [push [-p | --patch] [-S | --staged] [-k | --[no-]keep-index] [-q "
+"git stash [push] [-p | --patch] [-S | --staged] [-k | --[no-]keep-index] [-q "
 "| --quiet]\n"
 "          [-u | --include-untracked] [-a | --all] [(-m | --message) "
 "<message>]\n"
 "          [--pathspec-from-file=<file> [--pathspec-file-nul]]\n"
-"          [--] [<pathspec>...]]"
+"          [--] [<pathspec>...]"
 msgstr ""
-"git stash [push [-p | --patch] [-S | --staged] [-k | --[no-]keep-index] [-q "
+"git stash [push] [-p | --patch] [-S | --staged] [-k | --[no-]keep-index] [-q "
 "| --quiet]\n"
 "          [-u | --include-untracked] [-a | --all] [(-m | --message) "
 "<message>]\n"
 "          [--pathspec-from-file=<fichier> [--pathspec-file-nul]]\n"
-"          [--] [<spécificateur-de-chemin>...]]"
+"          [--] [<spécificateur-de-chemin>...]"
 
 msgid ""
 "git stash save [-p | --patch] [-S | --staged] [-k | --[no-]keep-index] [-q | "
@@ -13922,6 +14242,9 @@
 msgid "could not get a repository handle for submodule '%s'"
 msgstr "impossible de trouver une poignée de dépôt pour le sous-module '%s'"
 
+msgid "git submodule--helper get-default-remote <path>"
+msgstr "git submodule--helper get-default-remote <chemin>"
+
 #, c-format
 msgid "No url found for submodule path '%s' in .gitmodules"
 msgstr "URL non trouvée pour le chemin de sous-module '%s' dans .gitmodules"
@@ -13959,6 +14282,16 @@
 msgstr "git submodule foreach [--quiet] [--recursive] [--] <commande>"
 
 #, c-format
+msgid ""
+"failed to set a valid default config for 'submodule.%s.gitdir'. Please "
+"ensure it is set, for example by running something like: 'git config "
+"submodule.%s.gitdir .git/modules/%s'"
+msgstr ""
+"échec de réglage d'une valeur par défaut valide pour 'submodule.%s.gitdir'. "
+"Veuillez vous assurer qu'elle est réglée en lançant par exemple quelque "
+"chose comme : 'git config submodule.%s.gitdir .git/modules/%s'"
+
+#, c-format
 msgid "Failed to register url for submodule path '%s'"
 msgstr "Échec d'enregistrement de l'URL pour le chemin de sous-module '%s'"
 
@@ -14050,6 +14383,27 @@
 msgid "could not fetch a revision for HEAD"
 msgstr "impossible de récupérer une révision pour HEAD"
 
+msgid "git submodule--helper gitdir <name>"
+msgstr "git submodule--helper gitdir <nom>"
+
+msgid ""
+"could not set core.repositoryformatversion to 1.\n"
+"Please set it for migration to work, for example:\n"
+"git config core.repositoryformatversion 1"
+msgstr ""
+"impossible de régler core.repositoryformatversion à 1.\n"
+"Veuillez le faire pour que la migration réussisse, par exemple :\n"
+"git config core.repositoryformatversion 1"
+
+msgid ""
+"could not enable submodulePathConfig extension. It is required\n"
+"for migration to work. Please enable it in the root repo:\n"
+"git config extensions.submodulePathConfig true"
+msgstr ""
+"impossible d'activer l'extension submodulePathConfig. Elle est nécessaire\n"
+"pour que la migration réussisse. Veuillez l'activer dans le dépôt racine :\n"
+"git config extensions.submodulePathConfig true"
+
 #, c-format
 msgid "Synchronizing submodule url for '%s'\n"
 msgstr "Synchronisation de l'URL sous-module pour '%s'\n"
@@ -14147,11 +14501,6 @@
 msgstr "La valeur '%s' pour submodule.alternateLocation n'est pas reconnue"
 
 #, c-format
-msgid "refusing to create/use '%s' in another submodule's git dir"
-msgstr ""
-"refus de créer/utiliser '%s' dans un répertoire git d'un autre sous-module"
-
-#, c-format
 msgid "directory not empty: '%s'"
 msgstr "le répertoire n'est pas vide : '%s'"
 
@@ -14160,6 +14509,14 @@
 msgstr "le clonage de '%s' dans le chemin de sous-module '%s' a échoué"
 
 #, c-format
+msgid ""
+"refusing to create/use '%s' in another submodule's git dir. Enabling "
+"extensions.submodulePathConfig should fix this."
+msgstr ""
+"refus de créer/utiliser '%s' dans un répertoire git d'un autre sous-module. "
+"Activer extensions.submodulePathConfig devrait résoudre cela."
+
+#, c-format
 msgid "could not get submodule directory for '%s'"
 msgstr "impossible de créer le répertoire de sous-module pour '%s'"
 
@@ -15074,8 +15431,8 @@
 msgid "report pruned working trees"
 msgstr "afficher les arbres de travail élagués"
 
-msgid "expire working trees older than <time>"
-msgstr "faire expirer les arbres de travail plus vieux que <temps>"
+msgid "prune missing working trees older than <time>"
+msgstr "élaguer les arbres de travail plus vieux que <temps>"
 
 #, c-format
 msgid "'%s' already exists"
@@ -15146,15 +15503,8 @@
 msgid "Preparing worktree (detached HEAD %s)"
 msgstr "Préparation de l'arbre de travail (HEAD détachée %s)"
 
-#, c-format
-msgid ""
-"HEAD points to an invalid (or orphaned) reference.\n"
-"HEAD path: '%s'\n"
-"HEAD contents: '%s'"
-msgstr ""
-"HEAD pointe sur une référence invalide (ou orpheline).\n"
-"chemin de HEAD '%s'\n"
-"contenu de HEAD : '%s'"
+msgid "HEAD points to an invalid (or orphaned) reference.\n"
+msgstr "HEAD pointe sur une référence invalide (ou orpheline).\n"
 
 msgid ""
 "No local or remote refs exist despite at least one remote\n"
@@ -15216,9 +15566,10 @@
 msgid "show extended annotations and reasons, if available"
 msgstr "afficher les annotations étendues et les raisons, si disponible"
 
-msgid "add 'prunable' annotation to worktrees older than <time>"
+msgid "add 'prunable' annotation to missing worktrees older than <time>"
 msgstr ""
-"ajouter l'annotation 'prunable' aux arbres de travail plus vieux que <temps>"
+"ajouter l'annotation 'prunable' aux arbres de travail manquants plus vieux "
+"que <temps>"
 
 msgid "terminate records with a NUL character"
 msgstr "terminer les enregistrements par un caractère NUL"
@@ -15723,6 +16074,9 @@
 msgid "Display help information about Git"
 msgstr "Afficher l'information d'aide à propos de Git"
 
+msgid "EXPERIMENTAL: Rewrite history"
+msgstr "EXPERIMENTAL : Réécrit l'historique"
+
 msgid "Run git hooks"
 msgstr "Lance les crochets git"
 
@@ -15833,8 +16187,8 @@
 msgid "Pack heads and tags for efficient repository access"
 msgstr "Empaqueter les têtes et les étiquettes pour un accès efficace au dépôt"
 
-msgid "Compute unique ID for a patch"
-msgstr "Calculer l'ID unique d'une rustine"
+msgid "Compute unique IDs for patches"
+msgstr "Calculer des IDs uniques pour les rustines"
 
 msgid "Prune all unreachable objects from the object database"
 msgstr "Élaguer les objets inatteignables depuis la base de données des objets"
@@ -16335,11 +16689,11 @@
 
 #, c-format
 msgid ""
-"attempting to write a commit-graph, but 'commitGraph.changedPathsVersion' "
-"(%d) is not supported"
+"attempting to write a commit-graph, but 'commitGraph."
+"changedPathsVersion' (%d) is not supported"
 msgstr ""
-"essai d'écriture de graphe de commits, mais "
-"'commitGraph.changedPathsVersion' (%d) n'est pas pris en charge"
+"essai d'écriture de graphe de commits, mais 'commitGraph."
+"changedPathsVersion' (%d) n'est pas pris en charge"
 
 msgid "too many commits to write graph"
 msgstr "trop de commits pour écrire un graphe"
@@ -16417,6 +16771,10 @@
 msgstr "impossible d'analyser le commit %s"
 
 #, c-format
+msgid "object %s is a %s, not a %s"
+msgstr "l'objet %s est de type %s, pas de type %s"
+
+#, c-format
 msgid "%s %s is not a commit!"
 msgstr "%s %s n'est pas un commit !"
 
@@ -16828,10 +17186,6 @@
 "valeur numérique de configuration incorrecte '%s' pour '%s' dans %s : %s"
 
 #, c-format
-msgid "bad boolean config value '%s' for '%s'"
-msgstr "valeur booléenne de configuration invalide '%s' pour '%s'"
-
-#, c-format
 msgid "failed to expand user dir in: '%s'"
 msgstr "impossible d'étendre le répertoire utilisateur dans : '%s'"
 
@@ -17525,6 +17879,9 @@
 msgid "unknown value after ws-error-highlight=%.*s"
 msgstr "valeur inconnue après ws-error-highlight=%.*s"
 
+msgid "--find-object requires a git repository"
+msgstr "--find-object requiert un dépôt git"
+
 #, c-format
 msgid "unable to resolve '%s'"
 msgstr "impossible de résoudre '%s'"
@@ -17924,9 +18281,6 @@
 msgid "<depth>"
 msgstr "<profondeur>"
 
-msgid "maximum tree depth to recurse"
-msgstr "profondeur d'arbre maximum de récursion"
-
 msgid "<file>"
 msgstr "<fichier>"
 
@@ -18279,6 +18633,10 @@
 msgid "git fetch-pack: expected response end packet"
 msgstr "git fetch-pack : paquet de fin de réponse attendu"
 
+#, c-format
+msgid "couldn't resolve 'auto' filter '%s': %s"
+msgstr "impossible de résoudre le filtre 'auto' '%s' : %s"
+
 msgid "no matching remote head"
 msgstr "pas de HEAD distante correspondante"
 
@@ -18697,8 +19055,20 @@
 "You can disable this warning with `git config set advice.ignoredHook false`."
 msgstr ""
 "Le crochet '%s' a été ignoré parce qu'il n'est pas marqué comme exécutable.\n"
-"Vous pouvez désactiver cet avertissement avec `git config set "
-"advice.ignoredHook false`."
+"Vous pouvez désactiver cet avertissement avec `git config set advice."
+"ignoredHook false`."
+
+#, c-format
+msgid "disabled hook '%s' has no command configured"
+msgstr "le crochet désactivé '%s' n'a pas de commande configurée"
+
+#, c-format
+msgid ""
+"'hook.%s.command' must be configured or 'hook.%s.event' must be removed; "
+"aborting."
+msgstr ""
+"'hook.%s.command' doit être configuré ou 'hook.%s.event' doit être "
+"supprimé ; abandon."
 
 msgid "not a git repository"
 msgstr "pas un dépôt git"
@@ -18754,6 +19124,25 @@
 
 #, c-format
 msgid ""
+"response requested a delay greater than http.maxRetryTime (%ld > %ld seconds)"
+msgstr ""
+"La réponse à demandé un délai plus grand que http.maxRetryTime (%ld > %ld "
+"secondes)"
+
+#, c-format
+msgid ""
+"configured http.retryAfter exceeds http.maxRetryTime (%ld > %ld seconds)"
+msgstr ""
+"la configuration http.retryAfter est plus grande que http.maxRetryTime (%ld "
+"> %ld secondes)"
+
+#, c-format
+msgid "rate limited, waiting %ld seconds before retry"
+msgstr ""
+"limitation du débit, en attente pendant %ld secondes avant de réessayer"
+
+#, c-format
+msgid ""
 "number too large to represent as curl_off_t on this platform: %<PRIuMAX>"
 msgstr ""
 "nombre trop grand pour être représenté par curl_off_t sur cette plateforme : "
@@ -18836,6 +19225,9 @@
 "régler le dossier cible avec 'git config imap.folder <dossier>'\"\n"
 "(par ex., 'git config imap.folder Drafts')"
 
+msgid "'auto' filter not supported by this command"
+msgstr "filtre 'auto' non pris en charge par cette commande"
+
 msgid "expected 'tree:<depth>'"
 msgstr "attendu : 'tree:<profondeur>'"
 
@@ -18855,12 +19247,18 @@
 msgstr ""
 "le caractère doit être échappé dans le spécificateur de sous-filtre : '%c'"
 
+msgid "an 'auto' filter cannot be combined"
+msgstr "un filtre 'auto' ne peut pas être combiné"
+
 msgid "expected something after combine:"
 msgstr "quelque chose attendu après combine :"
 
 msgid "multiple filter-specs cannot be combined"
 msgstr "impossible de combiner des spécificateurs multiples de filtre"
 
+msgid "an 'auto' filter is incompatible with any other filter"
+msgstr "un filtre 'auto'  est incompatible avec d'autres filtres"
+
 msgid "unable to upgrade repository format to support partial clone"
 msgstr ""
 "impossible de mettre à jour le format de dépôt pour supporter les clones "
@@ -18895,22 +19293,44 @@
 msgstr "impossible de charger l'arbre racine pour le commit %s"
 
 #, c-format
+msgid "could not write lock pid file '%s'"
+msgstr "impossible d'écrire le fichier de verrou pid'%s'"
+
+#, c-format
+msgid "malformed lock pid file '%s'"
+msgstr "fichier de verrou pid malformé '%s'"
+
+#, c-format
 msgid ""
-"Unable to create '%s.lock': %s.\n"
+"Unable to create '%s': %s.\n"
 "\n"
-"Another git process seems to be running in this repository, e.g.\n"
-"an editor opened by 'git commit'. Please make sure all processes\n"
-"are terminated then try again. If it still fails, a git process\n"
-"may have crashed in this repository earlier:\n"
-"remove the file manually to continue."
 msgstr ""
-"Impossible de créer '%s.lock' : %s.\n"
+"Impossible de créer '%s' : %s.\n"
 "\n"
-"Il semble qu'un autre processus git est déjà lancé dans ce dépôt,\n"
-"par exemple un éditeur ouvert par 'git commit'. Veuillez vous assurer\n"
-"que tous les processus sont terminés et réessayez. Si l'échec persiste,\n"
-"un processus git peut avoir planté :\n"
-"supprimez le fichier manuellement pour poursuivre."
+
+#, c-format
+msgid ""
+"Lock may be held by process %<PRIuMAX>; if no git process is running, the "
+"lock file may be stale (PIDs can be reused)"
+msgstr ""
+"Le verrou peut être tenu par le processus %<PRIuMAX> ; si aucun processus "
+"git n'est en cours, le fichier de verrou peut être obsolète (les PIDs "
+"peuvent être réutilisés)"
+
+#, c-format
+msgid ""
+"Lock was held by process %<PRIuMAX>, which is no longer running; the lock "
+"file appears to be stale"
+msgstr ""
+"Le verrou était tenu par le processus %<PRIuMAX> qui n'est plus en cours, le "
+"fichier de verrou semble obsolète"
+
+msgid ""
+"Another git process seems to be running in this repository, or the lock file "
+"may be stale"
+msgstr ""
+"Un autre processus git semble en cours dans ce dépôt, ou le fichier verrou "
+"est obsolète"
 
 #, c-format
 msgid "Unable to create '%s.lock': %s"
@@ -19224,8 +19644,15 @@
 msgid "malformed line: %s"
 msgstr "ligne malformée : %s"
 
-msgid "could not load pack"
-msgstr "impossible de charger le paquet"
+#, c-format
+msgid "could not load pack %d"
+msgstr "impossible de charger le paquet %d"
+
+msgid "too many packs, unable to compact"
+msgstr "trop de paquets, impossible de compacter"
+
+msgid "could not determine preferred pack"
+msgstr "impossible de déterminer le paquet préféré"
 
 #, c-format
 msgid "unable to link '%s' to '%s'"
@@ -19235,6 +19662,13 @@
 msgid "failed to clear multi-pack-index at %s"
 msgstr "échec du nettoyage de l'index de multi-paquet à %s"
 
+#, c-format
+msgid "unknown MIDX version: %d"
+msgstr "version de MIDX inconnue : %d"
+
+msgid "cannot perform MIDX compaction with v1 format"
+msgstr "impossible de compacter les MIDX en format v1"
+
 msgid "ignoring existing multi-pack-index; checksum mismatch"
 msgstr ""
 "index multi-paquet existant ignoré ; non-concordance de la somme de contrôle"
@@ -19832,10 +20266,6 @@
 msgstr "type d'objet invalide \"%s\""
 
 #, c-format
-msgid "object %s is a %s, not a %s"
-msgstr "l'objet %s est de type %s, pas de type %s"
-
-#, c-format
 msgid "object %s has unknown type id %d"
 msgstr "l'objet %s a un id de type inconnu %d"
 
@@ -19844,6 +20274,10 @@
 msgstr "impossible d'analyser l'objet : %s"
 
 #, c-format
+msgid "unable to open object stream for %s"
+msgstr "impossible d'ouvrir l'objet flux pour %s"
+
+#, c-format
 msgid "hash mismatch %s"
 msgstr "incohérence d'empreinte %s"
 
@@ -19860,15 +20294,6 @@
 msgid "%s: ignoring alternate object stores, nesting too deep"
 msgstr "%s : magasins d'objets alternatifs ignorés, récursion trop profonde"
 
-msgid "unable to fdopen alternates lockfile"
-msgstr "impossible d'ouvrir (fdopen) le fichier verrou des alternatives"
-
-msgid "unable to read alternates file"
-msgstr "lecture du fichier d'alternatives impossible"
-
-msgid "unable to move new alternates file into place"
-msgstr "impossible de déplacer le nouveau fichier d'alternative"
-
 #, c-format
 msgid "path '%s' does not exist"
 msgstr "le chemin '%s' n'existe pas"
@@ -19915,6 +20340,15 @@
 msgid "%s is not a valid '%s' object"
 msgstr "%s n'est pas un objet '%s' valide"
 
+msgid "unable to fdopen alternates lockfile"
+msgstr "impossible d'ouvrir (fdopen) le fichier verrou des alternatives"
+
+msgid "unable to read alternates file"
+msgstr "lecture du fichier d'alternatives impossible"
+
+msgid "unable to move new alternates file into place"
+msgstr "impossible de déplacer le nouveau fichier d'alternative"
+
 #, c-format
 msgid "duplicate entry when writing bitmap index: %s"
 msgstr "entrée dupliquée dans l'index en bitmap : '%s'"
@@ -20129,9 +20563,6 @@
 msgstr ""
 "le tronçon d'index inversé de l'index multi-paquet n'a pas la bonne taille"
 
-msgid "could not determine preferred pack"
-msgstr "impossible de déterminer le paquet préféré"
-
 msgid "cannot both write and verify reverse index"
 msgstr "impossible de lire et vérifier à la fois l'index inverse"
 
@@ -20155,6 +20586,10 @@
 msgstr "le fichier paquet %s ne peut être mmap%s"
 
 #, c-format
+msgid "could not load .mtimes for cruft pack '%s'"
+msgstr "impossible de charger .mtimes pour le paquet déchet '%s'"
+
+#, c-format
 msgid "offset before start of pack index for %s (corrupt index?)"
 msgstr "offset avant le début de l'index de paquet pour %s (index corrompu ?)"
 
@@ -20509,6 +20944,10 @@
 msgid "unable to parse --pretty format"
 msgstr "impossible d'analyser le format --pretty"
 
+#, c-format
+msgid "%s is not supported by this command"
+msgstr "%s n'est pas pris en charge par cette commande"
+
 msgid "lazy fetching disabled; some objects may not be available"
 msgstr ""
 "récupération paresseuse désactivée ; certains objets pourraient ne pas être "
@@ -20556,6 +20995,22 @@
 msgstr "les serveur a annoncé un distant prometteur sans nom ou URL : %s"
 
 #, c-format
+msgid ""
+"Storing new %s from server for remote '%s'.\n"
+"    '%s' -> '%s'\n"
+msgstr ""
+"Stockage d'un nouveau %s depuis le server pour le distant '%s'.\n"
+"    '%s' -> '%s'\n"
+
+#, c-format
+msgid "invalid filter '%s' for remote '%s' will not be stored: %s"
+msgstr "le filtre invalide '%s' pour le distant '%s' ne sera pas stocké : %s"
+
+#, c-format
+msgid "invalid token '%s' for remote '%s' will not be stored"
+msgstr "le jeton invalide '%s' pour le distant '%s' ne sera pas stocké"
+
+#, c-format
 msgid "unknown '%s' value for '%s' config option"
 msgstr "valeur inconnue '%s' pour l'option de config '%s'"
 
@@ -20563,6 +21018,10 @@
 msgid "accepted promisor remote '%s' not found"
 msgstr "distant accpté de prometteur '%s' non trouvé"
 
+#, c-format
+msgid "promisor remote '%s' advertised invalid filter '%s': %s"
+msgstr "le prometteur distant '%s' a annoncé un filtre invalide '%s' : %s"
+
 msgid "object-info: expected flush after arguments"
 msgstr "object-info : vidage attendu après les arguments"
 
@@ -20822,6 +21281,14 @@
 msgstr "%s : impossible de revenir à l'étape 0"
 
 #, c-format
+msgid ""
+"Skipping submodule due to ignore=all: %s\n"
+"Use --force if you really want to add the submodule."
+msgstr ""
+"sous-module ignoré par ignore=all: %s\n"
+"Utilisez --force si vous souhaitez vraiment ajouter le sous-module."
+
+#, c-format
 msgid "unexpected diff status %c"
 msgstr "état de diff inattendu %c"
 
@@ -21200,6 +21667,12 @@
 msgid "no reflog for '%s'"
 msgstr "pas de journal de références pour '%s'"
 
+#, c-format
+msgid "in '%s' phase, update aborted by the reference-transaction hook"
+msgstr ""
+"dans la phase '%s', la mise à jour a été abandonnée par le crochet reference-"
+"transaction"
+
 msgid "Checking references consistency"
 msgstr "Vérification de la cohérence des références"
 
@@ -21309,9 +21782,6 @@
 msgid "ref updates forbidden inside quarantine environment"
 msgstr "mises à jour des références interdites en environnement de quarantaine"
 
-msgid "ref updates aborted by hook"
-msgstr "mises à jour des références annulées par le crochet"
-
 #, c-format
 msgid "'%s' exists; cannot create '%s'"
 msgstr "'%s' existe ; impossible de créer '%s'"
@@ -21517,6 +21987,14 @@
 "impossible d'accéder à '%s' avec la configuration http.pinnedPubkey : %s"
 
 #, c-format
+msgid "rate limited by '%s', please try again in %ld seconds"
+msgstr "débit limité par '%s', veuillez essayer dans %ld secondes"
+
+#, c-format
+msgid "rate limited by '%s', please try again later"
+msgstr "débit limité par '%s', veuillez essayer plus tard"
+
+#, c-format
 msgid "unable to access '%s': %s"
 msgstr "impossible d'accéder à '%s' : %s"
 
@@ -21532,8 +22010,8 @@
 
 msgid "unable to rewind rpc post data - try increasing http.postBuffer"
 msgstr ""
-"impossible de rembobiner le données post rpc - essayer d'augmenter "
-"http.postBuffer"
+"impossible de rembobiner le données post rpc - essayer d'augmenter http."
+"postBuffer"
 
 #, c-format
 msgid "remote-curl: bad line length character: %.4s"
@@ -21805,11 +22283,12 @@
 msgstr "* Référence bizarre '%s' ignorée localement"
 
 #, c-format
-msgid "Your branch is based on '%s', but the upstream is gone.\n"
-msgstr "Votre branche est basée sur '%s', mais la branche amont a disparu.\n"
-
-msgid "  (use \"git branch --unset-upstream\" to fixup)\n"
-msgstr "  (utilisez \"git branch --unset-upstream\" pour corriger)\n"
+msgid ""
+"ignoring value '%s' for status.compareBranches, only @{upstream} and @{push} "
+"are supported"
+msgstr ""
+"valeur '%s' ignorée par status.compareBranches, seuls @{upstream} et @{push} "
+"sont pris en charge"
 
 #, c-format
 msgid "Your branch is up to date with '%s'.\n"
@@ -21866,6 +22345,13 @@
 "  (utilisez \"git pull\" pour intégrer la branche distante avec la vôtre)\n"
 
 #, c-format
+msgid "Your branch is based on '%s', but the upstream is gone.\n"
+msgstr "Votre branche est basée sur '%s', mais la branche amont a disparu.\n"
+
+msgid "  (use \"git branch --unset-upstream\" to fixup)\n"
+msgstr "  (utilisez \"git branch --unset-upstream\" pour corriger)\n"
+
+#, c-format
 msgid "cannot parse expected object name '%s'"
 msgstr "impossible d'analyser le nom attendu d'objet '%s'"
 
@@ -21949,6 +22435,39 @@
 msgid "replace depth too high for object %s"
 msgstr "profondeur de remplacement trop grande pour l'objet %s"
 
+#, c-format
+msgid "'%s' is not a valid commit-ish for %s"
+msgstr "'%s' n'est pas un commit-esque valide pour l'option %s"
+
+#, c-format
+msgid "argument to %s must be a reference"
+msgstr "l'argument de %s doit être une référence"
+
+#, c-format
+msgid ""
+"'%s' cannot be used with multiple revision ranges because the ordering would "
+"be ill-defined"
+msgstr ""
+"'%s' ne peut pas être utilisé avec des plages de révisions multiples parce "
+"que l'ordre ne serait pas total"
+
+msgid "need some commits to replay"
+msgstr "commits requis pour pouvoir rejouer"
+
+msgid "all positive revisions given must be references"
+msgstr "toutes les révisions positives fournies doivent être des références"
+
+msgid "'--ref' cannot be used with multiple revision ranges"
+msgstr "--ref ne peut pas être utilisé avec des plages de révisions multiples"
+
+#, c-format
+msgid "'%s' is not a valid refname"
+msgstr "'%s' n'est pas un nom valide de référence"
+
+msgid "compatibility hash algorithm support requires Rust"
+msgstr ""
+"la prise en charge de la compatibilité d'algorithme d'empreinte requiert Rust"
+
 msgid "corrupt MERGE_RR"
 msgstr "MERGE_RR corrompu"
 
@@ -22687,10 +23206,6 @@
 msgstr "'%s' n'est pas un label valide"
 
 #, c-format
-msgid "'%s' is not a valid refname"
-msgstr "'%s' n'est pas un nom valide de référence"
-
-#, c-format
 msgid "update-ref requires a fully qualified refname e.g. refs/heads/%s"
 msgstr ""
 "update-ref requiert un nom de référence totalement qualifié par ex. refs/"
@@ -22781,6 +23296,15 @@
 msgid "cannot revert during a cherry-pick."
 msgstr "impossible d'annuler un commit pendant un picorage."
 
+msgid "trailers file contains empty line"
+msgstr "le fichier de lignes terminales contient une ligne vide"
+
+msgid "trailers file is empty"
+msgstr "le fichier de lignes terminales est vide"
+
+msgid "cannot read trailers files"
+msgstr "impossible de lire le fichier de lignes terminales"
+
 msgid "unusable squash-onto"
 msgstr "\"écrase-sur\" inutilisable"
 
@@ -23272,6 +23796,14 @@
 "\n"
 "\tgit config --global --add safe.directory %s"
 
+#, c-format
+msgid "error reading '%s'"
+msgstr "erreur à la lecture de '%s'"
+
+#, c-format
+msgid "not a regular file: '%s'"
+msgstr "%s n'est pas un fichier normal"
+
 msgid "Unable to read current working directory"
 msgstr "Impossible d'accéder au répertoire de travail courant"
 
@@ -23298,6 +23830,10 @@
 msgstr "impossible d'utiliser le dépôt nu '%s' (safe.bareRepository vaut '%s')"
 
 #, c-format
+msgid "unknown ref storage format: '%s'"
+msgstr "Format de stockage de réf inconnu : '%s'"
+
+#, c-format
 msgid ""
 "problem with core.sharedRepository filemode value (0%.3o).\n"
 "The owner of files must always have read and write permissions."
@@ -23643,10 +24179,6 @@
 msgstr "impossible de trouve le nom pour le sous-module '%s'"
 
 #, c-format
-msgid "refusing to move '%s' into an existing git dir"
-msgstr "refus de déplacer '%s' dans une répertoire git existant"
-
-#, c-format
 msgid ""
 "Migrating git directory of '%s%s' from\n"
 "'%s' to\n"
@@ -23664,6 +24196,30 @@
 msgstr "ls-tree a renvoyé un code de retour inattendu %d"
 
 #, c-format
+msgid ""
+"the 'submodule.%s.gitdir' config does not exist for module '%s'. Please "
+"ensure it is set, for example by running something like: 'git config "
+"submodule.%s.gitdir .git/modules/%s'. For details see the extensions."
+"submodulePathConfig documentation."
+msgstr ""
+"la config 'submodule.%s.gitdir' n'existe par pour le module '%s'. Veuillez "
+"vérifier qu'elle est réglée, en lançant par exemple quelque chose comme : "
+"'git config submodule.%s.gitdir .git/modules/%s'. Pour plus de détails, "
+"reportez-vous à la documentation d'extensions.submodulePathConfig."
+
+msgid ""
+"enabling extensions.submodulePathConfig might fix the following error, if "
+"it's not already enabled."
+msgstr ""
+"activer extensions.submodulePathConfig devrait régler l'erreur suivante, si "
+"elle n'est pas déjà activée."
+
+#, c-format
+msgid "refusing to create/use '%s' in another submodule's  git dir."
+msgstr ""
+"refus de créer/utiliser '%s' dans un répertoire git d'un autre sous-module"
+
+#, c-format
 msgid "failed to lstat '%s'"
 msgstr "échec du lstat de '%s'"
 
@@ -23716,6 +24272,10 @@
 msgid "too many commits marked reachable"
 msgstr "trop de commits marqués joignables"
 
+#, c-format
+msgid "could not find MIDX with checksum %s"
+msgstr "impossible de trouver le MIDX avec l'empreinte '%s'"
+
 msgid "could not determine MIDX preferred pack"
 msgstr "impossible de déterminer le paquet préféré de MIDX"
 
@@ -23812,6 +24372,28 @@
 msgid "empty trailer token in trailer '%.*s'"
 msgstr "symbole vide dans la ligne de fin '%.*s'"
 
+msgid "empty --trailer argument"
+msgstr "argument --trailer vide"
+
+#, c-format
+msgid "invalid trailer '%s': missing key before separator"
+msgstr "ligne terminale '%s' invalide : clé manquante avec le séparateur"
+
+#, c-format
+msgid "could not stat %s"
+msgstr "stat impossible de %s"
+
+#, c-format
+msgid "file %s is not a regular file"
+msgstr "%s n'est pas un fichier régulier"
+
+#, c-format
+msgid "file %s is not writable by user"
+msgstr "le fichier %s n'est pas inscriptible par l'utilisateur"
+
+msgid "could not write to temporary file"
+msgstr "impossible d'écrire dans un fichier temporaire"
+
 msgid "full write to remote helper failed"
 msgstr "échec de l'écriture totale sur l'assistant distant"
 
@@ -25185,8 +25767,14 @@
 "Les fichiers suivants sont 8bit mais ne déclarent pas de champs Content-"
 "Transfer-Encoding.\n"
 
-msgid "Which 8bit encoding should I declare [UTF-8]? "
-msgstr "Quel encodage 8bit doit être déclaré [UTF8] ? "
+msgid "Declare which 8bit encoding to use [default: UTF-8]? "
+msgstr "Déclarer l'encodage 8bit à utilisé [par défaut : UTF8] ?"
+
+#, perl-format
+msgid "'%s' does not appear to be a valid charset name. Use it anyway [y/N]? "
+msgstr ""
+"'%s' ne semble être un jeu de caractères valide. L'utiliser quand même [y/"
+"N] ?"
 
 #, perl-format
 msgid ""
@@ -25226,6 +25814,10 @@
 msgid "CA path \"%s\" does not exist"
 msgstr "Le chemin vers la CA \"%s\" n'existe pas"
 
+#, perl-format
+msgid "Only client key \"%s\" specified"
+msgstr "Seule la clé client \"%s\" a été spécifiée"
+
 msgid ""
 "    The Cc list above has been expanded by additional\n"
 "    addresses found in the patch commit message. By default\n"
diff --git a/po/ga.po b/po/ga.po
index 4c05a25..52b5b92 100644
--- a/po/ga.po
+++ b/po/ga.po
@@ -7,9 +7,9 @@
 msgstr ""
 "Project-Id-Version: Git\n"
 "Report-Msgid-Bugs-To: Git Mailing List <git@vger.kernel.org>\n"
-"POT-Creation-Date: 2026-01-22 23:57+0000\n"
-"PO-Revision-Date: 2026-01-23 10:47+0000\n"
-"Last-Translator: Aindriú Mac Giolla Eoin <aindriu80@gmail.com>\n"
+"POT-Creation-Date: 2026-04-08 23:26+0000\n"
+"PO-Revision-Date: 2026-04-09 15:12+0100\n"
+"Last-Translator: Aindriú Mac Giolla Eoin <aindriu80@yahoo.com>\n"
 "Language-Team: none\n"
 "Language: ga\n"
 "MIME-Version: 1.0\n"
@@ -19,10 +19,6 @@
 "X-Generator: Poedit 3.8\n"
 
 #, c-format
-msgid "%s cannot be negative"
-msgstr "Ní féidir %s a bheith diúltach"
-
-#, c-format
 msgid "Huh (%s)?"
 msgstr "Huh (%s)?"
 
@@ -179,20 +175,20 @@
 msgstr "Slán.\n"
 
 #, c-format
-msgid "Stage mode change [y,n,q,a,d%s,?]? "
-msgstr "Athrú modh stáitse [y, n, q, a, d%s,?]? "
+msgid "Stage mode change%s [y,n,q,a,d%s,?]? "
+msgstr "Athrú ar mhodh stáitse%s [y,n,q,a,d%s,?]? "
 
 #, c-format
-msgid "Stage deletion [y,n,q,a,d%s,?]? "
-msgstr "Scriosadh céime [y,n,q,a,d%s,?]? "
+msgid "Stage deletion%s [y,n,q,a,d%s,?]? "
+msgstr "Scriosadh céime %s [y,n,q,a,d%s,?]? "
 
 #, c-format
-msgid "Stage addition [y,n,q,a,d%s,?]? "
-msgstr "Breiseán céime [y, n, q, a, d%s,?]? "
+msgid "Stage addition%s [y,n,q,a,d%s,?]? "
+msgstr "Breis céime %s [y,n,q,a,d%s,?]? "
 
 #, c-format
-msgid "Stage this hunk [y,n,q,a,d%s,?]? "
-msgstr "Cuir an píosa seo ar stáitse [y,n,q,a,d%s,?]? "
+msgid "Stage this hunk%s [y,n,q,a,d%s,?]? "
+msgstr "Cuir an píosa seo ar stáitse %s [y,n,q,a,d%s,?]? "
 
 msgid ""
 "If the patch applies cleanly, the edited hunk will immediately be marked for "
@@ -216,20 +212,20 @@
 "chéile\n"
 
 #, c-format
-msgid "Stash mode change [y,n,q,a,d%s,?]? "
-msgstr "Athrú modh stash [y, n, q, a, d%s,?]? "
+msgid "Stash mode change%s [y,n,q,a,d%s,?]? "
+msgstr "Athrú ar mhodh stash%s [y,n,q,a,d%s,?]? "
 
 #, c-format
-msgid "Stash deletion [y,n,q,a,d%s,?]? "
-msgstr "Scriosadh staise [y,n,q,a,d%s,?]? "
+msgid "Stash deletion%s [y,n,q,a,d%s,?]? "
+msgstr "Scriosadh stash%s [y,n,q,a,d%s,?]? "
 
 #, c-format
-msgid "Stash addition [y,n,q,a,d%s,?]? "
-msgstr "Breiseán stash [y, n, q, a, d%s,?]? "
+msgid "Stash addition%s [y,n,q,a,d%s,?]? "
+msgstr "Breis stash%s [y,n,q,a,d%s,?]? "
 
 #, c-format
-msgid "Stash this hunk [y,n,q,a,d%s,?]? "
-msgstr "An bhfuil an carachtar seo [y,n,q,a,d%s,?] i bhfolach? "
+msgid "Stash this hunk%s [y,n,q,a,d%s,?]? "
+msgstr "An bhfuil an stuif seo %s [y,n,q,a,d%s,?] curtha i bhfolach agat? "
 
 msgid ""
 "If the patch applies cleanly, the edited hunk will immediately be marked for "
@@ -253,20 +249,20 @@
 "stóráil\n"
 
 #, c-format
-msgid "Unstage mode change [y,n,q,a,d%s,?]? "
-msgstr "Athrú ar mhodh gan stáitse [y, n, q, a, d%s,?]? "
+msgid "Unstage mode change%s [y,n,q,a,d%s,?]? "
+msgstr "Athrú ar mhodh neamhstáitseála%s [y,n,q,a,d%s,?]? "
 
 #, c-format
-msgid "Unstage deletion [y,n,q,a,d%s,?]? "
-msgstr "Scriosadh gan stáitse [y,n,q,a,d%s,?]? "
+msgid "Unstage deletion%s [y,n,q,a,d%s,?]? "
+msgstr "Scriosadh gan stáitse %s [y,n,q,a,d%s,?]? "
 
 #, c-format
-msgid "Unstage addition [y,n,q,a,d%s,?]? "
-msgstr "Breiseán gan stáitse [y, n, q, a, d%s,?]? "
+msgid "Unstage addition%s [y,n,q,a,d%s,?]? "
+msgstr "Breiseadh gan stáitse %s [y,n,q,a,d%s,?]? "
 
 #, c-format
-msgid "Unstage this hunk [y,n,q,a,d%s,?]? "
-msgstr "Dí-stáitseáil an píosa beag seo den stáitse [y,n,q,a,d%s,?]? "
+msgid "Unstage this hunk%s [y,n,q,a,d%s,?]? "
+msgstr "An bhfuil an píosa %s seo le baint den stáitse [y,n,q,a,d%s,?]? "
 
 msgid ""
 "If the patch applies cleanly, the edited hunk will immediately be marked for "
@@ -290,20 +286,20 @@
 "a dhíchur\n"
 
 #, c-format
-msgid "Apply mode change to index [y,n,q,a,d%s,?]? "
-msgstr "Cuir athrú mód i bhfeidhm ar innéacs [y,n,q,a,d%s,?]? "
+msgid "Apply mode change to index%s [y,n,q,a,d%s,?]? "
+msgstr "Cuir athrú mód i bhfeidhm ar innéacs %s [y,n,q,a,d%s,?]? "
 
 #, c-format
-msgid "Apply deletion to index [y,n,q,a,d%s,?]? "
-msgstr "Cuir scriosadh i bhfeidhm ar innéacs [y, n, q, a, d%s,?]? "
+msgid "Apply deletion to index%s [y,n,q,a,d%s,?]? "
+msgstr "Cuir scriosadh i bhfeidhm ar innéacs %s [y,n,q,a,d%s,?]? "
 
 #, c-format
-msgid "Apply addition to index [y,n,q,a,d%s,?]? "
-msgstr "Cuir an breiseán i bhfeidhm ar innéacs [y,n,q,a,d%s,?]? "
+msgid "Apply addition to index%s [y,n,q,a,d%s,?]? "
+msgstr "Cuir an breiseán i bhfeidhm ar innéacs %s [y,n,q,a,d%s,?]? "
 
 #, c-format
-msgid "Apply this hunk to index [y,n,q,a,d%s,?]? "
-msgstr "Cuir an píosa seo i bhfeidhm ar innéacs [y,n,q,a,d%s,?]? "
+msgid "Apply this hunk to index%s [y,n,q,a,d%s,?]? "
+msgstr "Cuir an píosa beag seo i bhfeidhm ar innéacs %s [y,n,q,a,d%s,?]? "
 
 msgid ""
 "If the patch applies cleanly, the edited hunk will immediately be marked for "
@@ -328,21 +324,21 @@
 "bhfeidhm\n"
 
 #, c-format
-msgid "Discard mode change from worktree [y,n,q,a,d%s,?]? "
-msgstr "Athrú modh a dhiúscairt ó chrann oibre [y, n, q, a, d%s,?]? "
+msgid "Discard mode change from worktree%s [y,n,q,a,d%s,?]? "
+msgstr "An bhfuil athrú mód ó chrann oibre%s [y,n,q,a,d%s,?] le fáil réidh? "
 
 #, c-format
-msgid "Discard deletion from worktree [y,n,q,a,d%s,?]? "
-msgstr "An scriosadh ón gcrann oibre [y,n,q,a,d%s,?] a sheachaint? "
+msgid "Discard deletion from worktree%s [y,n,q,a,d%s,?]? "
+msgstr "An bhfuil scriosadh ón gcrann oibre %s [y,n,q,a,d%s,?] le fáil réidh? "
 
 #, c-format
-msgid "Discard addition from worktree [y,n,q,a,d%s,?]? "
-msgstr "Scrios an breiseán ón gcrann oibre [y,n,q,a,d%s,?]? "
+msgid "Discard addition from worktree%s [y,n,q,a,d%s,?]? "
+msgstr "Scrios an breiseán ón gcrann oibre%s [y,n,q,a,d%s,?]? "
 
 #, c-format
-msgid "Discard this hunk from worktree [y,n,q,a,d%s,?]? "
+msgid "Discard this hunk from worktree%s [y,n,q,a,d%s,?]? "
 msgstr ""
-"An bhfuil an píosa beag seo le fáil réidh ón gcrann oibre [y,n,q,a,d%s,?]? "
+"An bhfuil an píosa beag seo le fáil réidh ó chrann oibre%s [y,n,q,a,d%s,?]? "
 
 msgid ""
 "If the patch applies cleanly, the edited hunk will immediately be marked for "
@@ -366,24 +362,27 @@
 "chomhad\n"
 
 #, c-format
-msgid "Discard mode change from index and worktree [y,n,q,a,d%s,?]? "
+msgid "Discard mode change from index and worktree%s [y,n,q,a,d%s,?]? "
 msgstr ""
-"Athrú modh a dhiúscairt ó innéacs agus crann oibre [y, n, q, a, d %s,?]? "
+"An bhfuil athrú mód ón innéacs agus crann oibre%s [y,n,q,a,d%s,?] le fáil "
+"réidh? "
 
 #, c-format
-msgid "Discard deletion from index and worktree [y,n,q,a,d%s,?]? "
+msgid "Discard deletion from index and worktree%s [y,n,q,a,d%s,?]? "
 msgstr ""
-"An scriosadh ón innéacs agus ón gcrann oibre [y,n,q,a,d%s,?] a dhíbirt? "
+"An bhfuil an scriosadh ón innéacs agus ón gcrann oibre %s [y,n,q,a,d%s,?] le "
+"fáil réidh? "
 
 #, c-format
-msgid "Discard addition from index and worktree [y,n,q,a,d%s,?]? "
-msgstr "Caitheamh breisiú ó innéacs agus crann oibre [y, n, q, a, d %s,?]? "
+msgid "Discard addition from index and worktree%s [y,n,q,a,d%s,?]? "
+msgstr ""
+"Scrios an breiseán ón innéacs agus ón gcrann oibre %s [y,n,q,a,d%s,?]? "
 
 #, c-format
-msgid "Discard this hunk from index and worktree [y,n,q,a,d%s,?]? "
+msgid "Discard this hunk from index and worktree%s [y,n,q,a,d%s,?]? "
 msgstr ""
-"An bhfuil an píosa beag seo le fáil réidh ón innéacs agus ón gcrann oibre [y,"
-"n,q,a,d%s,?]? "
+"An bhfuil an píosa beag seo le fáil réidh ón innéacs agus ón gcrann oibre%s "
+"[y,n,q,a,d%s,?]? "
 
 msgid ""
 "y - discard this hunk from index and worktree\n"
@@ -400,27 +399,27 @@
 "chomhad\n"
 
 #, c-format
-msgid "Apply mode change to index and worktree [y,n,q,a,d%s,?]? "
+msgid "Apply mode change to index and worktree%s [y,n,q,a,d%s,?]? "
 msgstr ""
-"Cuir athrú mód i bhfeidhm ar an innéacs agus ar an gcrann oibre [y,n,q,a,"
-"d%s,?]? "
+"Cuir athrú mód i bhfeidhm ar an innéacs agus ar an gcrann oibre%s "
+"[y,n,q,a,d%s,?]? "
 
 #, c-format
-msgid "Apply deletion to index and worktree [y,n,q,a,d%s,?]? "
+msgid "Apply deletion to index and worktree%s [y,n,q,a,d%s,?]? "
 msgstr ""
-"Cuir scriosadh i bhfeidhm ar innéacs agus crann oibre [y, n, q, a, d%s,?]? "
+"Cuir scriosadh i bhfeidhm ar innéacs agus ar chrann oibre%s [y,n,q,a,d%s,?]? "
 
 #, c-format
-msgid "Apply addition to index and worktree [y,n,q,a,d%s,?]? "
+msgid "Apply addition to index and worktree%s [y,n,q,a,d%s,?]? "
 msgstr ""
-"Cuir an breiseán i bhfeidhm ar an innéacs agus ar an gcrann oibre [y,n,q,a,"
-"d%s,?]? "
+"Cuir an breiseán i bhfeidhm ar innéacs agus ar chrann oibre%s "
+"[y,n,q,a,d%s,?]? "
 
 #, c-format
-msgid "Apply this hunk to index and worktree [y,n,q,a,d%s,?]? "
+msgid "Apply this hunk to index and worktree%s [y,n,q,a,d%s,?]? "
 msgstr ""
-"Cuir an píosa seo i bhfeidhm ar an innéacs agus ar an gcrann oibre [y,n,q,a,"
-"d%s,?]? "
+"Cuir an píosa seo i bhfeidhm ar innéacs agus ar chrann oibre%s "
+"[y,n,q,a,d%s,?]? "
 
 msgid ""
 "y - apply this hunk to index and worktree\n"
@@ -438,20 +437,20 @@
 "bhfeidhm\n"
 
 #, c-format
-msgid "Apply mode change to worktree [y,n,q,a,d%s,?]? "
-msgstr "Cuir athrú mód i bhfeidhm ar an gcrann oibre [y,n,q,a,d%s,?]? "
+msgid "Apply mode change to worktree%s [y,n,q,a,d%s,?]? "
+msgstr "Cuir athrú mód i bhfeidhm ar chrann oibre%s [y,n,q,a,d%s,?]? "
 
 #, c-format
-msgid "Apply deletion to worktree [y,n,q,a,d%s,?]? "
-msgstr "Cuir scriosadh i bhfeidhm ar chrann oibre [y, n, q, a, d%s,?]? "
+msgid "Apply deletion to worktree%s [y,n,q,a,d%s,?]? "
+msgstr "Cuir scriosadh i bhfeidhm ar chrann oibre%s [y,n,q,a,d%s,?]? "
 
 #, c-format
-msgid "Apply addition to worktree [y,n,q,a,d%s,?]? "
-msgstr "Cuir an breiseán i bhfeidhm ar an gcrann oibre [y,n,q,a,d%s,?]? "
+msgid "Apply addition to worktree%s [y,n,q,a,d%s,?]? "
+msgstr "Cuir an breiseán i bhfeidhm ar chrann oibre%s [y,n,q,a,d%s,?]? "
 
 #, c-format
-msgid "Apply this hunk to worktree [y,n,q,a,d%s,?]? "
-msgstr "Cuir an píosa seo i bhfeidhm ar an gcrann oibre [y,n,q,a,d%s,?]? "
+msgid "Apply this hunk to worktree%s [y,n,q,a,d%s,?]? "
+msgstr "Cuir an píosa seo i bhfeidhm ar worktree%s [y,n,q,a,d%s,?]? "
 
 msgid ""
 "y - apply this hunk to worktree\n"
@@ -469,6 +468,10 @@
 "bhfeidhm\n"
 
 #, c-format
+msgid "%s cannot be negative"
+msgstr "Ní féidir %s a bheith diúltach"
+
+#, c-format
 msgid "could not parse hunk header '%.*s'"
 msgstr "níorbh fhéidir ceanntásc an bhlúire '%.*s' a pharsáil"
 
@@ -565,6 +568,7 @@
 msgid "Nothing was applied.\n"
 msgstr "Ní chuirtear aon rud i bhfeidhm.\n"
 
+#, c-format
 msgid ""
 "j - go to the next undecided hunk, roll over at the bottom\n"
 "J - go to the next hunk, roll over at the bottom\n"
@@ -576,26 +580,47 @@
 "e - manually edit the current hunk\n"
 "p - print the current hunk\n"
 "P - print the current hunk using the pager\n"
+"> - go to the next file, roll over at the bottom\n"
+"< - go to the previous file, roll over at the top\n"
 "? - print help\n"
+"HUNKS SUMMARY - Hunks: %d, USE: %d, SKIP: %d\n"
 msgstr ""
 "j - téigh go dtí an chéad phíosa eile gan chinneadh, rolladh thar ag an "
 "mbun\n"
 "J - téigh go dtí an chéad phíosa eile, rolladh thar ag an mbun\n"
-"k - téigh go dtí an phíosa roimhe sin gan chinneadh, rolladh thar ag an "
+"k - téigh go dtí an píosa roimhe sin gan chinneadh, rolladh thar ag an "
 "mbarr\n"
-"K - téigh go dtí an phíosa roimhe sin, rolladh thar ag an mbarr\n"
+"K - téigh go dtí an píosa roimhe sin, rolladh thar ag an mbarr\n"
 "g - roghnaigh píosa le dul chuige\n"
 "/ - déan cuardach ar phíosa a mheaitseálann an regex tugtha\n"
 "s - roinn an píosa reatha ina phíosaí níos lú\n"
 "e - cuir an píosa reatha in eagar de láimh\n"
 "p - priontáil an píosa reatha\n"
-"P - priontáil an píosa reatha ag baint úsáide as an ngléasóir\n"
-"? - cabhair phriontála\n"
+"P - priontáil an píosa reatha ag baint úsáide as an pager\n"
+"> - téigh go dtí an chéad chomhad eile, rolladh thar ag an mbun\n"
+"< - téigh go dtí an comhad roimhe seo, rolladh thar ag an mbarr\n"
+"? - priontáil cabhair\n"
+"ACHOIMRE PÍOSAÍ - Píosaí: %d, ÚSÁID: %d, SCIP: %d\n"
+
+msgid "'git apply' failed"
+msgstr "Theip ar 'git apply'"
+
+msgid " (was: y)"
+msgstr " (bhí: y)"
+
+msgid " (was: n)"
+msgstr " (bhí: n)"
 
 #, c-format
 msgid "Only one letter is expected, got '%s'"
 msgstr "Níl súil leis ach litir amháin, fuair '%s'"
 
+msgid "No next file"
+msgstr "Níl aon chomhad eile ann"
+
+msgid "No previous file"
+msgstr "Gan aon chomhad roimhe seo"
+
 msgid "No other hunk"
 msgstr "Gan aon phíosa eile"
 
@@ -649,9 +674,6 @@
 msgid "Unknown command '%s' (use '?' for help)"
 msgstr "Ordú anaithnid '%s' (bain úsáid as '?' le haghaidh cabhair)"
 
-msgid "'git apply' failed"
-msgstr "Theip ar 'git apply'"
-
 msgid "No changes."
 msgstr "Gan aon athruithe."
 
@@ -659,6 +681,25 @@
 msgstr "Níor athraigh ach comhaid dénártha."
 
 #, c-format
+msgid "Stage mode change [y,n,q,a,d%s,?]? "
+msgstr "Athrú modh stáitse [y, n, q, a, d%s,?]? "
+
+#, c-format
+msgid "Stage deletion [y,n,q,a,d%s,?]? "
+msgstr "Scriosadh céime [y,n,q,a,d%s,?]? "
+
+#, c-format
+msgid "Stage addition [y,n,q,a,d%s,?]? "
+msgstr "Breiseán céime [y, n, q, a, d%s,?]? "
+
+#, c-format
+msgid "Stage this hunk [y,n,q,a,d%s,?]? "
+msgstr "Cuir an píosa seo ar stáitse [y,n,q,a,d%s,?]? "
+
+msgid "Revision does not refer to a commit"
+msgstr "Ní thagraíonn athbhreithniú do thiomantas"
+
+#, c-format
 msgid ""
 "\n"
 "Disable this message with \"git config set advice.%s false\""
@@ -860,14 +901,28 @@
 msgstr "d'fhill regexec %d le haghaidh ionchur: %s"
 
 #, c-format
-msgid "unable to find filename in patch at line %d"
-msgstr "ní féidir ainm comhaid a aimsiú i bpaiste ag an líne %d"
+msgid "unable to find filename in patch at %s:%d"
+msgstr "ní féidir ainm comhaid a aimsiú sa phaiste ag %s:%d"
+
+#, c-format
+msgid "git apply: bad git-diff - expected /dev/null, got %s at %s:%d"
+msgstr ""
+"git apply: git-diff lochtach - bhíothas ag súil le /dev/null, fuair mé %s ag "
+"%s:%d"
 
 #, c-format
 msgid "git apply: bad git-diff - expected /dev/null, got %s on line %d"
 msgstr "git apply: bad git-diff - ag súil le /dev/null, fuair %s ar líne %d"
 
 #, c-format
+msgid "git apply: bad git-diff - inconsistent new filename at %s:%d"
+msgstr "git apply: git-diff lochtach - ainm comhaid nua neamhréireach ag %s:%d"
+
+#, c-format
+msgid "git apply: bad git-diff - inconsistent old filename at %s:%d"
+msgstr "git apply: git-diff lochtach - seanainm comhaid neamhréireach ag %s:%d"
+
+#, c-format
 msgid "git apply: bad git-diff - inconsistent new filename on line %d"
 msgstr ""
 "git apply: bad git-diff - ainm comhaid nua neamhchomhsheasmhach ar líne %d"
@@ -877,10 +932,18 @@
 msgstr "git apply: bad git-diff - sean-ainm comhaid neamhréireach ar líne %d"
 
 #, c-format
+msgid "git apply: bad git-diff - expected /dev/null at %s:%d"
+msgstr "git apply: git-diff lochtach - bhíothas ag súil le /dev/null ag %s:%d"
+
+#, c-format
 msgid "git apply: bad git-diff - expected /dev/null on line %d"
 msgstr "git apply: bad git-diff - súil leis /dev/null ar líne %d"
 
 #, c-format
+msgid "invalid mode at %s:%d: %s"
+msgstr "mód neamhbhailí ag %s:%d: %s"
+
+#, c-format
 msgid "invalid mode on line %d: %s"
 msgstr "modh neamhbhailí ar líne %d: %s"
 
@@ -891,6 +954,23 @@
 #, c-format
 msgid ""
 "git diff header lacks filename information when removing %d leading pathname "
+"component at %s:%d"
+msgid_plural ""
+"git diff header lacks filename information when removing %d leading pathname "
+"components at %s:%d"
+msgstr[0] ""
+"tá easpa eolais ainm comhaid ar cheanntásc git diff nuair a bhaintear %d "
+"comhpháirt ainm cosáin tosaigh ag %s:%d"
+msgstr[1] ""
+"tá easpa eolais ainm comhaid ar cheanntásc git diff agus %d comhpháirteanna "
+"ainm cosáin tosaigh á mbaint ag %s:%d"
+msgstr[2] ""
+"tá easpa eolais ainm comhaid ar cheanntásc git diff agus %d comhpháirteanna "
+"ainm cosáin tosaigh á mbaint ag %s:%d"
+
+#, c-format
+msgid ""
+"git diff header lacks filename information when removing %d leading pathname "
 "component (line %d)"
 msgid_plural ""
 "git diff header lacks filename information when removing %d leading pathname "
@@ -906,6 +986,10 @@
 "ainm cosáin tosaigh á mbaint (líne %d)"
 
 #, c-format
+msgid "git diff header lacks filename information at %s:%d"
+msgstr "tá easpa eolais ainm comhaid ar cheanntásc git diff ag %s:%d"
+
+#, c-format
 msgid "git diff header lacks filename information (line %d)"
 msgstr "tá easpa eolais ainm comhaid ar cheanntásc git diff (líne %d)"
 
@@ -914,8 +998,8 @@
 msgstr "atháireamh: líne gan choinne: %.*s"
 
 #, c-format
-msgid "patch fragment without header at line %d: %.*s"
-msgstr "blúirt paiste gan ceanntásc ag an líne %d: %.*s"
+msgid "patch fragment without header at %s:%d: %.*s"
+msgstr "blúire paiste gan cheanntásc ag %s:%d: %.*s"
 
 msgid "new file depends on old contents"
 msgstr "braitheann comhad nua ar shean-ábhar"
@@ -924,8 +1008,8 @@
 msgstr "tá ábhar fós ag comhad scriosta"
 
 #, c-format
-msgid "corrupt patch at line %d"
-msgstr "paiste truaillithe ag líne %d"
+msgid "corrupt patch at %s:%d"
+msgstr "paiste truaillithe ag %s:%d"
 
 #, c-format
 msgid "new file %s depends on old contents"
@@ -940,16 +1024,16 @@
 msgstr "** rabhadh: éiríonn comhad %s folamh ach ní scriostar é"
 
 #, c-format
-msgid "corrupt binary patch at line %d: %.*s"
-msgstr "paiste dénártha truaillithe ag líne %d: %.*s"
+msgid "corrupt binary patch at %s:%d: %.*s"
+msgstr "paiste dénártha truaillithe ag %s:%d: %.*s"
 
 #, c-format
-msgid "unrecognized binary patch at line %d"
-msgstr "paiste dénártha gan aithint ag an líne %d"
+msgid "unrecognized binary patch at %s:%d"
+msgstr "paiste dénártha neamhaitheanta ag %s:%d"
 
 #, c-format
-msgid "patch with only garbage at line %d"
-msgstr "paiste gan ach truflais ag an líne %d"
+msgid "patch with only garbage at %s:%d"
+msgstr "paiste le bruscar amháin ag %s:%d"
 
 #, c-format
 msgid "unable to read symlink %s"
@@ -1214,6 +1298,14 @@
 msgstr "ní féidir comhad innéacs a léamh"
 
 #, c-format
+msgid "option -p expects a non-negative integer, got '%s'"
+msgstr "rogha -p ag súil le slánuimhir neamh-dhiúltach, fuair '%s'"
+
+#, c-format
+msgid "unable to normalize directory: '%s'"
+msgstr "ní féidir an eolaire a normalú: '%s'"
+
+#, c-format
 msgid "can't open patch '%s': %s"
 msgstr "ní féidir paiste '%s' a oscailt: %s"
 
@@ -1913,6 +2005,11 @@
 msgid "select hunks interactively"
 msgstr "roghnaigh hunks idirghníomhach"
 
+msgid "auto advance to the next file when selecting hunks interactively"
+msgstr ""
+"dul ar aghaidh go huathoibríoch chuig an gcéad chomhad eile agus píosaí "
+"beaga á roghnú go hidirghníomhach"
+
 msgid "edit current diff and apply"
 msgstr "athraigh an dif reatha agus cuir i bhfeidhm"
 
@@ -2418,6 +2515,10 @@
 msgid "Restrict the missing objects to the current sparse-checkout"
 msgstr "Cuir srian ar na rudaí atá in easnamh don tseiceáil neamhchoitianta"
 
+#, c-format
+msgid "unrecognized argument: %s"
+msgstr "argóint gan aithint: %s"
+
 msgid ""
 "git bisect start [--term-(bad|new)=<term-new> --term-(good|old)=<term-old>]\n"
 "                 [--no-checkout] [--first-parent] [<bad> [<good>...]] [--] "
@@ -3114,11 +3215,11 @@
 msgstr "Ní fhaightear CEAD thíos na refs/heads!"
 
 msgid ""
-"branch with --recurse-submodules can only be used if submodule."
-"propagateBranches is enabled"
+"branch with --recurse-submodules can only be used if "
+"submodule.propagateBranches is enabled"
 msgstr ""
-"ní féidir brainse le --recurse-submodules a úsáid ach amháin má tá submodule."
-"propagateBranches cumasaithe"
+"ní féidir brainse le --recurse-submodules a úsáid ach amháin má tá "
+"submodule.propagateBranches cumasaithe"
 
 msgid "--recurse-submodules can only be used to create branches"
 msgstr "Ní féidir --recurse-submodules a úsáid ach chun brainsí a chruthú"
@@ -3620,18 +3721,6 @@
 msgid "copy out the files from named stage"
 msgstr "cóipeáil amach na comhaid ón gcéim ainmnithe"
 
-msgid "git checkout [<options>] <branch>"
-msgstr "git checkout [<roghanna>] <brainse>"
-
-msgid "git checkout [<options>] [<branch>] -- <file>..."
-msgstr "git checkout [<roghanna>] [<brainse>] --<comhad>..."
-
-msgid "git switch [<options>] [<branch>]"
-msgstr "git switch [<options>] [<branch>]"
-
-msgid "git restore [<options>] [--source=<branch>] <file>..."
-msgstr "git restore [<options>] [--source=<branch>] <file>..."
-
 #, c-format
 msgid "path '%s' does not have our version"
 msgstr "níl ár leagan ag cosán '%s'"
@@ -3830,27 +3919,26 @@
 "araon.\n"
 "Úsáid le do thoil -- (agus go roghnach --no-guess) chun a dhíbhriú"
 
+#, c-format
 msgid ""
 "If you meant to check out a remote tracking branch on, e.g. 'origin',\n"
 "you can do so by fully qualifying the name with the --track option:\n"
 "\n"
-"    git checkout --track origin/<name>\n"
+"    git %s --track origin/<name>\n"
 "\n"
 "If you'd like to always have checkouts of an ambiguous <name> prefer\n"
 "one remote, e.g. the 'origin' remote, consider setting\n"
 "checkout.defaultRemote=origin in your config."
 msgstr ""
-"Má bhí sé i gceist agat brainse cianrianaithe a sheiceáil ar, e.g. "
-"'origin',\n"
+"Más mian leat brainse rianaithe iargúlta a sheiceáil ar, e.g. 'origin',\n"
 "is féidir leat é sin a dhéanamh tríd an ainm a cháiliú go hiomlán leis an "
 "rogha --track:\n"
 "\n"
-"    git checkout --track origin/<name>\n"
+"git %s --track origin/<ainm>\n"
 "\n"
-"<name>Más mian leat seiceálacha débhríoch a bheith agat i gcónaí is fearr "
-"leat\n"
-"iargúlta amháin, e.g. an iargúlta 'origin', smaoinigh ar shocrú\n"
-"checkout.defaultRemote=origin i do chumraíocht."
+"Más mian leat go mbeadh rogha ag seiceálacha de <ainm> débhríoch i gcónaí\n"
+"d'aon chianrialtán amháin, e.g. an cianrialtán 'origin', smaoinigh ar\n"
+"checkout.defaultRemote=origin a shocrú i do chumraíocht."
 
 #, c-format
 msgid "'%s' matched multiple (%d) remote tracking branches"
@@ -3999,6 +4087,18 @@
 msgid "do not limit pathspecs to sparse entries only"
 msgstr "ná teorainn le speisiúintí cosáin le hiontrálacha neamhchoitianta"
 
+msgid "git checkout [<options>] <branch>"
+msgstr "git checkout [<roghanna>] <brainse>"
+
+msgid "git checkout [<options>] [<branch>] -- <file>..."
+msgstr "git checkout [<roghanna>] [<brainse>] --<comhad>..."
+
+msgid "git switch [<options>] [<branch>]"
+msgstr "git switch [<options>] [<branch>]"
+
+msgid "git restore [<options>] [--source=<branch>] <file>..."
+msgstr "git restore [<options>] [--source=<branch>] <file>..."
+
 #, c-format
 msgid "options '-%c', '-%c', and '%s' cannot be used together"
 msgstr "ní féidir na roghanna '-%c', '-%c', agus '%s' a úsáid le chéile"
@@ -5806,10 +5906,6 @@
 msgid "git diff-pairs -z [<diff-options>]"
 msgstr "git diff-pairs -z [<diff-options>]"
 
-#, c-format
-msgid "unrecognized argument: %s"
-msgstr "argóint gan aithint: %s"
-
 msgid "working without -z is not supported"
 msgstr "ní thacaítear le bheith ag obair gan -z"
 
@@ -5840,7 +5936,7 @@
 msgstr "stádas diff anaithnid: %c"
 
 msgid "--merge-base only works with two commits"
-msgstr "Ní oibríonn --merge-base ach le dhá thiomantas"
+msgstr "ní oibríonn --merge-base ach le dhá thiomantas"
 
 #, c-format
 msgid "'%s': not a regular file or symlink"
@@ -6030,13 +6126,6 @@
 "bhuail an tiomantas sínithe %s; bain úsáid as --signed-commits=<mode> chun é "
 "a láimhseáil"
 
-msgid ""
-"'strip-if-invalid' is not a valid mode for git fast-export with --signed-"
-"commits=<mode>"
-msgstr ""
-"'strip-if-invalid' ní mód bailí é seo le haghaidh easpórtáil le haghaidh git fast-export le --signed-"
-"commits=<mód>"
-
 #, c-format
 msgid ""
 "omitting tag %s,\n"
@@ -6063,13 +6152,6 @@
 "bhuail mé le clib shínithe %s; bain úsáid as --signed-tags=<mode> chun é a "
 "láimhseáil"
 
-msgid ""
-"'strip-if-invalid' is not a valid mode for git fast-export with --signed-"
-"tags=<mode>"
-msgstr ""
-"'strip-if-invalid' ní mód bailí é seo le haghaidh git fast-export le --signed-"
-"tags=<mode>"
-
 #, c-format
 msgid ""
 "tag %s tags unexported object; use --tag-of-filtered-object=<mode> to handle "
@@ -6489,6 +6571,39 @@
 "ag baint síniú neamhbhailí don tiomnú\n"
 "  de réir dealraimh ag %s"
 
+#, c-format
+msgid ""
+"replacing invalid signature for commit '%.100s...'\n"
+"  allegedly by %s"
+msgstr ""
+"ag cur síniú neamhbhailí in ionad an tiomantais '%.100s...'\n"
+"  de réir dealraimh le %s"
+
+#, c-format
+msgid ""
+"replacing invalid signature for commit '%.*s'\n"
+"  allegedly by %s"
+msgstr ""
+"ag cur síniú neamhbhailí in ionad an tiomantais '%.*s'\n"
+"  de réir dealraimh le %s"
+
+#, c-format
+msgid ""
+"replacing invalid signature for commit\n"
+"  allegedly by %s"
+msgstr ""
+"ag cur síniú neamhbhailí in ionad an tiomnaithe\n"
+" de réir dealraimh le %s"
+
+msgid "aborting due to invalid signature"
+msgstr "ag cur as oifig mar gheall ar shíniú neamhbhailí"
+
+msgid "signing commits in interoperability mode is unsupported"
+msgstr "ní thacaítear le síniú tiomantais i mód idir-inoibritheachta"
+
+msgid "failed to sign commit object"
+msgstr "theip ar an réad tiomnaithe a shíniú"
+
 msgid "expected committer but didn't get one"
 msgstr "bhí mé ag súil le gealltóir ach ní bhfuair mé ceann"
 
@@ -6503,6 +6618,9 @@
 msgid "importing a commit signature verbatim"
 msgstr "síniú tiomantais a allmhairiú focal ar fhocal"
 
+msgid "failed to sign tag object"
+msgstr "theip ar an réad clibe a shíniú"
+
 #, c-format
 msgid "importing a tag signature verbatim for tag '%s'"
 msgstr "ag allmhairiú síniú clibe focal ar fhocal don chlib '%s'"
@@ -6516,13 +6634,6 @@
 "bhuail mé le clib shínithe; bain úsáid as --signed-tags=<mode> chun é a "
 "láimhseáil"
 
-msgid ""
-"'strip-if-invalid' is not a valid mode for git fast-import with --signed-"
-"tags=<mode>"
-msgstr ""
-"Ní mód bailí é 'strip-if-invalid' le haghaidh git fast-import le --signed-"
-"tags=<mode>"
-
 #, c-format
 msgid "expected 'from' command, got '%s'"
 msgstr "ag súil le hordú 'from', fuarthas '%s'"
@@ -7048,8 +7159,8 @@
 msgstr "ní thacaíonn an prótacal le --negotiate-only, ag scoir"
 
 msgid ""
-"--filter can only be used with the remote configured in extensions."
-"partialclone"
+"--filter can only be used with the remote configured in "
+"extensions.partialclone"
 msgstr ""
 "--filter Ní féidir ach an scagaire a úsáid ach leis an iargúlta cumraithe in "
 "extensions.partialclone"
@@ -7584,8 +7695,8 @@
 msgid ""
 "skipping incremental-repack task because core.multiPackIndex is disabled"
 msgstr ""
-"ag scipeáil an tasc athphacála incriminteach mar go bhfuil core."
-"multiPackIndex díchumasaithe"
+"ag scipeáil an tasc athphacála incriminteach mar go bhfuil "
+"core.multiPackIndex díchumasaithe"
 
 msgid "failed to perform geometric repack"
 msgstr "theip ar athphacáil gheoiméadrach a dhéanamh"
@@ -7951,7 +8062,7 @@
 msgstr "líon neamhbhailí na snáitheanna sonraithe (%d)"
 
 msgid "--open-files-in-pager only works on the worktree"
-msgstr "Ní oibríonn --open-files-in-pager ach ar an gcrann oibre"
+msgstr "ní oibríonn --open-files-in-pager ach ar an gcrann oibre"
 
 msgid "--[no-]exclude-standard cannot be used for tracked contents"
 msgstr ""
@@ -8102,12 +8213,161 @@
 msgid "'git help config' for more information"
 msgstr "'git help config' le haghaidh tuilleadh faisnéise"
 
-msgid ""
-"git hook run [--ignore-missing] [--to-stdin=<path>] <hook-name> [-- <hook-"
-"args>]"
+msgid "git history reword <commit> [--dry-run] [--update-refs=(branches|head)]"
 msgstr ""
-"<path><hook-name><hook-args>git hook run [--ignore-missing] [--to-stdin =] "
-"[--]"
+"git history reword <commit> [--dry-run] [--update-refs=(brainsí|ceann)]"
+
+msgid ""
+"git history split <commit> [--dry-run] [--update-refs=(branches|head)] [--] "
+"[<pathspec>...]"
+msgstr ""
+"git history split <commit> [--dry-run] [--update-refs=(brainsí|ceann)] [--] "
+"[<sonraíocht cosáin>...]"
+
+#, c-format
+msgid ""
+"Please enter the commit message for the %s changes. Lines starting\n"
+"with '%s' will be ignored, and an empty message aborts the commit.\n"
+msgstr ""
+"Cuir isteach an teachtaireacht tiomantais do na hathruithe %s. Déanfar "
+"neamhaird ar\n"
+"línte a thosaíonn le '%s', agus cuirfidh teachtaireacht fholamh deireadh "
+"leis an tiomantas.\n"
+
+#, c-format
+msgid "Aborting commit as launching the editor failed.\n"
+msgstr ""
+"Ag cur deireadh leis an tiomnú mar theip ar an eagarthóir a sheoladh.\n"
+
+#, c-format
+msgid "unable to parse parent commit %s"
+msgstr "ní féidir an tiomnú tuismitheora %s a pharsáil"
+
+#, c-format
+msgid "%s expects one of 'branches' or 'head'"
+msgstr "Tá %s ag súil le ceann de na cinn seo a leanas: 'branches' nó 'head'"
+
+msgid "error preparing revisions"
+msgstr "earráid ag ullmhú athbhreith"
+
+msgid "replaying merge commits is not supported yet!"
+msgstr "ní thacaítear le gealltanna cumaisc athsheinm fós!"
+
+msgid "cannot look up HEAD"
+msgstr "ní féidir breathnú suas CEANN"
+
+msgid "cannot determine descendance"
+msgstr "ní féidir an sliocht a chinneadh"
+
+msgid ""
+"rewritten commit must be an ancestor of HEAD when using --update-refs=head"
+msgstr ""
+"ní mór don tiomnú athscríofa a bheith ina shinsear de HEAD nuair a úsáidtear "
+"--update-refs=head"
+
+#, c-format
+msgid "failed to begin ref transaction: %s"
+msgstr "theip ar thosú an idirbhirt tagartha: %s"
+
+#, c-format
+msgid "failed to update ref '%s': %s"
+msgstr "theip ar an tagairt '%s' a nuashonrú: %s"
+
+#, c-format
+msgid "failed to commit ref transaction: %s"
+msgstr "theip ar an idirbheart tagartha a dhéanamh: %s"
+
+msgid "control which refs should be updated"
+msgstr "rialú cé na tagairtí ba chóir a nuashonrú"
+
+msgid "perform a dry-run without updating any refs"
+msgstr "déan tástáil gan aon tagairtí a nuashonrú"
+
+msgid "command expects a single revision"
+msgstr "tá athbhreithniú amháin ag teastáil ón ordú"
+
+#, c-format
+msgid "commit cannot be found: %s"
+msgstr "ní féidir an tiomantas a aimsiú: %s"
+
+msgid "failed writing reworded commit"
+msgstr "theip ar scríobh an tiomnaithe athfhoclaithe"
+
+msgid "failed replaying descendants"
+msgstr "theip ar athimirt sliocht"
+
+msgid "unable to populate index with tree"
+msgstr "ní féidir an t-innéacs a líonadh le crann"
+
+msgid "unable to acquire index lock"
+msgstr "ní féidir glas innéacs a fháil"
+
+msgid "failed reading temporary index"
+msgstr "theip ar an innéacs sealadach a léamh"
+
+msgid "failed split tree"
+msgstr "crann scoilte theip"
+
+msgid "split commit is empty"
+msgstr "tá an tiomantas scoilte folamh"
+
+msgid "split commit tree matches original commit"
+msgstr "crann tiomantais scoilte ag teacht leis an tiomantas bunaidh"
+
+msgid "failed writing first commit"
+msgstr "theip ar an gcéad tiomantas a scríobh"
+
+msgid "failed writing second commit"
+msgstr "theip ar an dara tiomantas a scríobh"
+
+msgid "control ref update behavior"
+msgstr "iompar nuashonraithe tag rialaithe"
+
+msgid "command expects a committish"
+msgstr "tá an t-ordú ag súil le comité"
+
+msgid "cannot split up merge commit"
+msgstr "ní féidir an tiomantas cumasc a roinnt"
+
+msgid ""
+"git hook run [--allow-unknown-hook-name] [--ignore-missing] [--to-"
+"stdin=<path>] <hook-name> [-- <hook-args>]"
+msgstr ""
+"git hook run [--allow-unknown-crúca-ainm] [--ignore-missing] [--to-"
+"stdin=<cosán>] <ainm-crúca> [-- <argóintí-crúca>]"
+
+msgid ""
+"git hook list [--allow-unknown-hook-name] [-z] [--show-scope] <hook-name>"
+msgstr ""
+"git hook list [--allow-unknown-crúca-ainm] [-z] [--show-scope] <ainm-crúca>"
+
+msgid "use NUL as line terminator"
+msgstr "bain úsáid as NUL mar chríochnóir líne"
+
+msgid "show the config scope that defined each hook"
+msgstr "taispeáin an raon feidhme cumraíochta a shainmhínigh gach crúca"
+
+msgid "allow running a hook with a non-native hook name"
+msgstr "cead a thabhairt do rith crúca le hainm crúca neamhdhúchasach"
+
+msgid "you must specify a hook event name to list"
+msgstr "ní mór duit ainm imeachta crúca a shonrú le liostáil"
+
+#, c-format
+msgid ""
+"unknown hook event '%s';\n"
+"use --allow-unknown-hook-name to allow non-native hook names"
+msgstr ""
+"teagmhas crúca anaithnid '%s';\n"
+"úsáid --allow-unknown-hook-name chun ainmneacha crúca neamhdhúchasacha a "
+"cheadú"
+
+#, c-format
+msgid "no hooks found for event '%s'"
+msgstr "gan aon crúcaí aimsithe don imeacht '%s'"
+
+msgid "hook from hookdir"
+msgstr "crúca ó hookdir"
 
 msgid "silently ignore missing requested <hook-name>"
 msgstr "neamhaird go ciúin a iarrtar ar <hook-name>"
@@ -8422,21 +8682,6 @@
 "                       [--parse] [<file>...]"
 
 #, c-format
-msgid "could not stat %s"
-msgstr "ní raibh ann %s a shástáil"
-
-#, c-format
-msgid "file %s is not a regular file"
-msgstr "ní comhad rialta é comhad %s"
-
-#, c-format
-msgid "file %s is not writable by user"
-msgstr "ní féidir an t-úsáideoir comhad %s a scríobh"
-
-msgid "could not open temporary file"
-msgstr "ní fhéadfadh comhad sealadach a oscailt"
-
-#, c-format
 msgid "could not read input file '%s'"
 msgstr "ní raibh in ann comhad ionchuir '%s' a léamh"
 
@@ -8444,6 +8689,10 @@
 msgstr "ní fhéadfaí léamh ó stdin"
 
 #, c-format
+msgid "could not write to temporary file '%s'"
+msgstr "níorbh fhéidir MIDX a aimsiú: %s"
+
+#, c-format
 msgid "could not rename temporary file to %s"
 msgstr "ní fhéadfaí comhad sealadach a athainmniú go %s"
 
@@ -8468,8 +8717,8 @@
 msgid "output only the trailers"
 msgstr "aschur ach na leantóirí"
 
-msgid "do not apply trailer.* configuration variables"
-msgstr "ná cuir leantóir i bhfeidhm.* athróga cumraíochta"
+msgid "do not apply trailer.<key-alias> configuration variables"
+msgstr "ná cuir athróga cumraíochta trailer.<key-alias> i bhfeidhm"
 
 msgid "reformat multiline trailer values as single-line values"
 msgstr "luachanna leantóra illíne a athfhormáidiú mar luachanna aon-líne"
@@ -8489,24 +8738,26 @@
 msgid "no input file given for in-place editing"
 msgstr "níl aon chomhad ionchuir a thugtar le haghaidh eagarthóireachta"
 
-msgid "last-modified can only operate on one tree at a time"
+msgid "last-modified can only operate on one commit at a time"
 msgstr ""
-"ní féidir leis an modhnaithe deireanach oibriú ach ar chrann amháin ag an am "
+"ní féidir leis an modhnú deireanach oibriú ach ar thiomantas amháin ag an am "
 "céanna"
 
 #, c-format
+msgid "revision argument '%s' is a %s, not a commit-ish"
+msgstr "is %s an argóint athbhreithnithe '%s', ní commit-ish"
+
+#, c-format
 msgid "unknown last-modified argument: %s"
 msgstr "argóint anaithnid a modhnaíodh go deireanach: %s"
 
-msgid "unable to setup last-modified"
-msgstr "ní féidir an modhnú deireanach a shocrú"
-
 msgid ""
-"git last-modified [--recursive] [--show-trees] [<revision-range>] [[--] "
-"<path>...]"
+"git last-modified [--recursive] [--show-trees] [--max-depth=<depth>] [-z]\n"
+"                  [<revision-range>] [[--] <pathspec>...]"
 msgstr ""
-"git last-modified [--recursive] [--show-trees] [<revision-range>] [[--] "
-"<path>...]"
+"git last-modified [--recursive] [--show-trees] [--max-depth=<doimhneacht>] [-"
+"z]\n"
+"                  [<raon-athbhreithnithe>] [[--] <sonraíocht-conaire>...]"
 
 msgid "recurse into subtrees"
 msgstr "athshlánú isteach i bhfo-chrainn"
@@ -8514,6 +8765,12 @@
 msgid "show tree entries when recursing into subtrees"
 msgstr "taispeáin iontrálacha crainn agus tú ag athfhillteach i bhfochrainn"
 
+msgid "maximum tree depth to recurse"
+msgstr "uasmhéid doimhneacht crainn le hathfhillteach"
+
+msgid "lines are separated with NUL character"
+msgstr "tá línte scartha le carachtar NUL"
+
 msgid "git log [<options>] [<revision-range>] [[--] <path>...]"
 msgstr "git log [<options>] [<revision-range>] [[--] <path>...]"
 
@@ -8586,6 +8843,19 @@
 msgstr "format.headers gan luach"
 
 #, c-format
+msgid "bad boolean config value '%s' for '%s'"
+msgstr "droch-luach cumraíochta boolean '%s' do '%s'"
+
+#, c-format
+msgid ""
+"'%s' used to accept any value and treat that as 'true'.\n"
+"Now it only accepts boolean values, like what '%s' does.\n"
+msgstr ""
+"Ghlacfadh '%s' le haon luach agus dhéanfadh sé é sin a mheas mar 'fíor'.\n"
+"Anois ní ghlacann sé ach le luachanna Booleánacha, díreach mar a dhéanann "
+"'%s'.\n"
+
+#, c-format
 msgid "cannot open patch file %s"
 msgstr "ní féidir comhad paiste %s a oscailt"
 
@@ -8606,6 +8876,10 @@
 msgstr "theip ar chomhad litir chlúdaigh a chruthú"
 
 #, c-format
+msgid "'%s' is not a valid format string"
+msgstr "Ní teaghrán formáide bailí é '%s'"
+
+#, c-format
 msgid "insane in-reply-to: %s"
 msgstr "in-fhreagairt daingniúil: %s"
 
@@ -8667,6 +8941,13 @@
 msgid "generate a cover letter"
 msgstr "litir chlúdaigh a ghiniúint"
 
+msgid "format-spec"
+msgstr "sonraíocht formáide"
+
+msgid "format spec used for the commit list in the cover letter"
+msgstr ""
+"sonraíocht fhormáide a úsáidtear don liosta tiomantais sa litir chumhdaigh"
+
 msgid "use simple number sequence for output file names"
 msgstr "úsáid seicheamh uimhreacha simplí d'ainmneacha comhaid aschu"
 
@@ -9550,11 +9831,20 @@
 msgstr "cead níos mó ná crann amháin a chruthú"
 
 msgid ""
-"git multi-pack-index [<options>] write [--preferred-pack=<pack>][--refs-"
-"snapshot=<path>]"
+"git multi-pack-index [<options>] write [--preferred-pack=<pack>]\n"
+"  [--[no-]bitmap] [--[no-]incremental] [--[no-]stdin-packs]\n"
+"  [--refs-snapshot=<path>]"
 msgstr ""
-"git multi-pack-index [<options>] write [--preferred-pack=<pack>][--refs-"
-"snapshot=<path>]"
+"git multi-pack-index [<roghanna>] scríobh [--preferred-pack=<pacáiste>]\n"
+"  [--[gan-]bitmap] [--[gan-]incriminteach] [--[gan-]stdin-packs]\n"
+"  [--refs-snapshot=<cosán>]"
+
+msgid ""
+"git multi-pack-index [<options>] compact [--[no-]incremental]\n"
+"  [--[no-]bitmap] <from> <to>"
+msgstr ""
+"git multi-pack-index [<roghanna>] dlúth [--[gan-]incriminteach]\n"
+" [--[gan-]bitmap] <ó> <go>"
 
 msgid "git multi-pack-index [<options>] verify"
 msgstr "git multi-pack-index [<options>] verify"
@@ -9590,6 +9880,17 @@
 msgid "refs snapshot for selecting bitmap commits"
 msgstr "léargas refs chun gealltanais bitmap a roghnú"
 
+#, c-format
+msgid "could not find MIDX: %s"
+msgstr "níorbh fhéidir MIDX a aimsiú: %s"
+
+msgid "MIDX compaction endpoints must be unique"
+msgstr "Ní mór críochphointí dlúthaithe MIDX a bheith uathúil"
+
+#, c-format
+msgid "MIDX %s must be an ancestor of %s"
+msgstr "Caithfidh MIDX %s a bheith ina shinsear de %s"
+
 msgid ""
 "during repack, collect pack-files of smaller size into a batch that is "
 "larger than this size"
@@ -10097,8 +10398,8 @@
 
 msgid "disabling bitmap writing, packs are split due to pack.packSizeLimit"
 msgstr ""
-"scríobh bitmap a dhíchumasú, roinntear pacáistí mar gheall ar pack."
-"packSizeLimit"
+"scríobh bitmap a dhíchumasú, roinntear pacáistí mar gheall ar "
+"pack.packSizeLimit"
 
 msgid "Writing objects"
 msgstr "Rudaí a scríobh"
@@ -10205,14 +10506,14 @@
 msgstr "ní fhéadfaí cineál réada %s a fháil i bpacáiste %s"
 
 #, c-format
-msgid "packfile %s is a promisor but --exclude-promisor-objects was given"
-msgstr "is gealltóir é an comhad paca %s ach tugadh --exclude-promisor-objects"
-
-#, c-format
 msgid "could not find pack '%s'"
 msgstr "ní raibh an pacáiste '%s' in ann a aimsiú"
 
 #, c-format
+msgid "packfile %s is a promisor but --exclude-promisor-objects was given"
+msgstr "is gealltóir é an comhad paca %s ach tugadh --exclude-promisor-objects"
+
+#, c-format
 msgid "packfile %s cannot be accessed"
 msgstr "ní féidir teacht ar chomhad pacáiste %s"
 
@@ -10241,9 +10542,6 @@
 "aitheantas réada a bhfuil súil leis, fuair truflais:\n"
 " %s"
 
-msgid "could not load cruft pack .mtimes"
-msgstr "ní fhéadfaí pacáiste cruft a luchtú .mtimes"
-
 msgid "cannot open pack index"
 msgstr "ní féidir innéacs pacáiste a osc"
 
@@ -11904,8 +12202,8 @@
 msgstr ""
 "Tá tagairtí contrártha sa\n"
 "tagarmharc sprice nua ag an gcianrialtán atá tú ag iarraidh a athainmniú. Is "
-"dóichí gur mar gheall ar iarracht a dhéanamh cianrialtán a neadú ann féin, e."
-"g. trí 'tuismitheoir' a athainmniú go 'tuismitheoir/leanbh'\n"
+"dóichí gur mar gheall ar iarracht a dhéanamh cianrialtán a neadú ann féin, "
+"e.g. trí 'tuismitheoir' a athainmniú go 'tuismitheoir/leanbh'\n"
 "nó trí chianrialtán a dhí-neadú, e.g. an bealach eile.\n"
 "\n"
 "Más amhlaidh atá, is féidir leat é seo a réiteach tríd an\n"
@@ -12515,51 +12813,40 @@
 msgstr "ní féidir ach patrún amháin a thabhairt le -l"
 
 #, c-format
-msgid "'%s' is not a valid commit-ish for %s"
-msgstr "Ní comhartha bailí commit-ish é '%s' do %s"
-
-msgid "need some commits to replay"
-msgstr "teastaíonn roinnt gealltanais chun athsheinm"
-
-msgid "all positive revisions given must be references"
-msgstr "caithfidh gach athbhreithniú dearfach a thugtar a bheith ina"
-
-msgid "argument to --advance must be a reference"
-msgstr "caithfidh argóint chuig --advance a bheith ina thagairt"
-
-msgid ""
-"cannot advance target with multiple sources because ordering would be ill-"
-"defined"
-msgstr ""
-"ní féidir leis an sprioc a chur chun cinn le foinsí iolracha toisc go mbeadh "
-"ordú"
-
-#, c-format
 msgid "invalid %s value: '%s'"
 msgstr "luach neamhbhailí %s: '%s'"
 
 msgid ""
-"(EXPERIMENTAL!) git replay ([--contained] --onto <newbase> | --advance "
-"<branch>) [--ref-action[=<mode>]] <revision-range>"
+"(EXPERIMENTAL!) git replay ([--contained] --onto=<newbase> | --"
+"advance=<branch> | --revert=<branch>)\n"
+"[--ref=<ref>] [--ref-action=<mode>] <revision-range>"
 msgstr ""
-"(TURGNAMHACH!) athsheinm git ([--contained] --onto <newbase> | --advance "
-"<brainse>) [--ref-action[=<mód>]] <raon-athbhreithnithe>"
-
-msgid "make replay advance given branch"
-msgstr "athsheoladh a dhéanamh roimh ré brainse ar leith"
-
-msgid "replay onto given commit"
-msgstr "athsheoladh ar thiomantas a thugtar"
+"(TURGNAÍOCH!) athsheinm git ([--contained] --onto=<newbase> | --"
+"advance=<brainse> | --revert=<brainse>)\n"
+"[--ref=<ref>] [--ref-action=<mód>] <raon-athbhreithnithe>"
 
 msgid "update all branches that point at commits in <revision-range>"
 msgstr ""
 "nuashonraigh na brainsí uile a dhíríonn ar thiomnuithe i <revision-range>"
 
+msgid "replay onto given commit"
+msgstr "athsheoladh ar thiomantas a thugtar"
+
+msgid "make replay advance given branch"
+msgstr "athsheoladh a dhéanamh roimh ré brainse ar leith"
+
+msgid "revert commits onto given branch"
+msgstr "aisiompaigh gealltanais ar bhrainse áirithe"
+
+msgid "reference to update with result"
+msgstr "tagairt don nuashonrú le toradh"
+
 msgid "control ref update behavior (update|print)"
 msgstr "iompar nuashonraithe tag rialaithe (nuashonrú|priontáil)"
 
-msgid "option --onto or --advance is mandatory"
-msgstr "tá rogha --onto nó --advance éigeantach"
+msgid "exactly one of --onto, --advance, or --revert is required"
+msgstr ""
+"tá ceann amháin go díreach de --onto, --advance, nó --revert ag teastáil"
 
 #, c-format
 msgid ""
@@ -12570,30 +12857,12 @@
 "'%s' i 'struct rev_info' iallach"
 
 #, c-format
-msgid "failed to begin ref transaction: %s"
-msgstr "theip ar thosú an idirbhirt tagartha: %s"
-
-msgid "error preparing revisions"
-msgstr "earráid ag ullmhú athbhreith"
-
-msgid "replaying down from root commit is not supported yet!"
-msgstr "ní thacaítear le hathsheinm anuas ó fréamh-thiomantas go fóill!"
-
-msgid "replaying merge commits is not supported yet!"
-msgstr "ní thacaítear le gealltanna cumaisc athsheinm fós!"
-
-#, c-format
-msgid "failed to update ref '%s': %s"
-msgstr "theip ar an tagairt '%s' a nuashonrú: %s"
-
-#, c-format
-msgid "failed to commit ref transaction: %s"
-msgstr "theip ar an idirbheart tagartha a dhéanamh: %s"
-
-#, c-format
 msgid "key '%s' not found"
 msgstr "níor aimsíodh an eochair '%s'"
 
+msgid "--keys can only be used with --format=lines or --format=nul"
+msgstr "Ní féidir --keys a úsáid ach le --format=lines nó --format=nul"
+
 #, c-format
 msgid "invalid format '%s'"
 msgstr "formáid neamhbhailí '%s'"
@@ -12607,6 +12876,12 @@
 msgid "print all keys/values"
 msgstr "priontáil na heochracha/luachanna go léir"
 
+msgid "show keys"
+msgstr "taispeáin eochracha"
+
+msgid "--keys cannot be used with a <key> or --all"
+msgstr "Ní féidir --keys a úsáid le <eochair> nó --all"
+
 msgid "unsupported output format"
 msgstr "formáid aschuir neamhthacaithe"
 
@@ -12649,6 +12924,18 @@
 msgid "Disk size"
 msgstr "Méid diosca"
 
+msgid "Largest objects"
+msgstr "Na rudaí is mó"
+
+msgid "Maximum size"
+msgstr "Uasmhéid"
+
+msgid "Maximum parents"
+msgstr "Uasmhéid tuismitheoirí"
+
+msgid "Maximum entries"
+msgstr "Uasmhéid iontrálacha"
+
 msgid "Repository structure"
 msgstr "Struchtúr an stórais"
 
@@ -13237,6 +13524,46 @@
 msgid "Unknown hash algorithm"
 msgstr "Algartam hash anaithnid"
 
+msgid "assuming SHA-1; use --object-format to override"
+msgstr ""
+"ag glacadh leis go bhfuil SHA-1 ann; bain úsáid as --object-format chun é a "
+"shárú"
+
+msgid "unable to read header"
+msgstr "ní féidir ceanntásc a léamh"
+
+msgid "unknown index version"
+msgstr "leagan innéacs anaithnid"
+
+msgid "unable to read index"
+msgstr "gan an t-innéacs a léamh"
+
+msgid "corrupt index file"
+msgstr "comhad innéacs truaillithe"
+
+#, c-format
+msgid "unable to read entry %u/%u"
+msgstr "ní féidir iontráil %u/%u a léamh"
+
+#, c-format
+msgid "unable to read sha1 %u/%u"
+msgstr "ní féidir sha1 %u/%u a léamh"
+
+#, c-format
+msgid "unable to read crc %u/%u"
+msgstr "ní féidir crc %u/%u a léamh"
+
+#, c-format
+msgid "unable to read 32b offset %u/%u"
+msgstr "ní féidir an fhritháireamh 32b %u/%u a léamh"
+
+msgid "inconsistent 64b offset index"
+msgstr "innéacs fritháireamh 64b neamhréireach"
+
+#, c-format
+msgid "unable to read 64b offset %u"
+msgstr "ní féidir an fhritháireamh 64b %u a léamh"
+
 msgid ""
 "git show-ref [--head] [-d | --dereference]\n"
 "             [-s | --hash[=<n>]] [--abbrev[=<n>]] [--branches] [--tags]\n"
@@ -13492,18 +13819,19 @@
 msgstr "git stash store [(-m | --message) <message>] [-q | --quiet] <commit>"
 
 msgid ""
-"git stash [push [-p | --patch] [-S | --staged] [-k | --[no-]keep-index] [-q "
+"git stash [push] [-p | --patch] [-S | --staged] [-k | --[no-]keep-index] [-q "
 "| --quiet]\n"
 "          [-u | --include-untracked] [-a | --all] [(-m | --message) "
 "<message>]\n"
 "          [--pathspec-from-file=<file> [--pathspec-file-nul]]\n"
-"          [--] [<pathspec>...]]"
+"          [--] [<pathspec>...]"
 msgstr ""
-"git stash [bhrú [-p | --patch] [-S | --stage] [-k | -- [no-] coinneáil "
+"git stash [brúigh] [-p | --paiste] [-S | --staged] [-k | --[gan-]coinnigh-"
 "innéacs] [-q | --ciúin]\n"
-" <message>[-u | --include-untracked] [-a | --all] [(-m | --teachtaireacht)]\n"
-" <file>[--pathspec-ó-comhad = [--pathspec-comhad-nul]]\n"
-" [--] [<pathspec>...]]"
+"          [-u | --áirítear-gan-rianú] [-a | --uile] [(-m | --teachtaireacht) "
+"<teachtaireacht>]\n"
+"          [--conairspec-ó-chomhad=<comhad> [--conairspec-comhad-nul]]\n"
+"          [--] [<conairspec>...]"
 
 msgid ""
 "git stash save [-p | --patch] [-S | --staged] [-k | --[no-]keep-index] [-q | "
@@ -13767,6 +14095,9 @@
 msgid "could not get a repository handle for submodule '%s'"
 msgstr "ní fhéadfaí láimhseáil stór a fháil do fho-mhodúl '%s'"
 
+msgid "git submodule--helper get-default-remote <path>"
+msgstr "git submodule--helper get-default-remote <cosán>"
+
 #, c-format
 msgid "No url found for submodule path '%s' in .gitmodules"
 msgstr "Níl aon url le haghaidh cosán fo-mhodúil '%s' i .gitmodules"
@@ -13803,6 +14134,16 @@
 msgstr "git submodule foreach [--quiet] [--recursive] [--] <command>"
 
 #, c-format
+msgid ""
+"failed to set a valid default config for 'submodule.%s.gitdir'. Please "
+"ensure it is set, for example by running something like: 'git config "
+"submodule.%s.gitdir .git/modules/%s'"
+msgstr ""
+"theip ar chumraíocht réamhshocraithe bhailí a shocrú do 'submodule."
+"%s.gitdir'. Cinntigh go bhfuil sé socraithe, mar shampla trí rud éigin "
+"cosúil le: 'git config submodule.%s.gitdir .git/modules/%s' a rith"
+
+#, c-format
 msgid "Failed to register url for submodule path '%s'"
 msgstr "Theip ar url a chlárú le haghaidh cosán fo-mhodúil '%s'"
 
@@ -13891,6 +14232,27 @@
 msgid "could not fetch a revision for HEAD"
 msgstr "ní fhéadfaí athbhreithniú a fháil do HEAD"
 
+msgid "git submodule--helper gitdir <name>"
+msgstr "git submodule--helper gitdir <ainm>"
+
+msgid ""
+"could not set core.repositoryformatversion to 1.\n"
+"Please set it for migration to work, for example:\n"
+"git config core.repositoryformatversion 1"
+msgstr ""
+"níorbh fhéidir core.repositoryformatversion a shocrú go 1.\n"
+"Socraigh é le go n-oibreoidh an t-imirce, mar shampla:\n"
+"git config core.repositoryformatversion 1"
+
+msgid ""
+"could not enable submodulePathConfig extension. It is required\n"
+"for migration to work. Please enable it in the root repo:\n"
+"git config extensions.submodulePathConfig true"
+msgstr ""
+"níorbh fhéidir síneadh submodulePathConfig a chumasú. Tá sé riachtanach\n"
+"chun go n-oibreoidh an t-imirce. Cumasaigh é sa stór fréimhe le do thoil:\n"
+"git config extensions.submodulePathConfig fíor"
+
 #, c-format
 msgid "Synchronizing submodule url for '%s'\n"
 msgstr "Url fo-mhodúil a shioncrónú do '%s'\n"
@@ -13984,10 +14346,6 @@
 msgstr "Luach '%s' le haghaidh submodule.alternateLocation ní aithnítear"
 
 #, c-format
-msgid "refusing to create/use '%s' in another submodule's git dir"
-msgstr "diúltú '%s' a chruthú/úsáid i git dir fo-mhodúil eile"
-
-#, c-format
 msgid "directory not empty: '%s'"
 msgstr "eolaire nach folamh: '%s'"
 
@@ -13996,6 +14354,14 @@
 msgstr "theip ar chlón '%s' isteach i gcosán fo-mhodúil '%s'"
 
 #, c-format
+msgid ""
+"refusing to create/use '%s' in another submodule's git dir. Enabling "
+"extensions.submodulePathConfig should fix this."
+msgstr ""
+"ag diúltú '%s' a chruthú/a úsáid i n-eolaire git fo-mhodúil eile. Ba chóir "
+"go réiteofaí é seo trí extensions.submodulePathConfig a chumasú."
+
+#, c-format
 msgid "could not get submodule directory for '%s'"
 msgstr "ní fhéadfaí eolaire fo-mhodúil a fháil do '%s'"
 
@@ -14895,8 +15261,8 @@
 msgid "report pruned working trees"
 msgstr "crainn oibre gearrtha a thuairisciú"
 
-msgid "expire working trees older than <time>"
-msgstr "dul in éag crainn oibre níos sine ná <time>"
+msgid "prune missing working trees older than <time>"
+msgstr "bearradh crainn oibre atá ar iarraidh atá níos sine ná <am>"
 
 #, c-format
 msgid "'%s' already exists"
@@ -14965,15 +15331,8 @@
 msgid "Preparing worktree (detached HEAD %s)"
 msgstr "Crann oibre a ullmhú (CEAD scoite %s)"
 
-#, c-format
-msgid ""
-"HEAD points to an invalid (or orphaned) reference.\n"
-"HEAD path: '%s'\n"
-"HEAD contents: '%s'"
-msgstr ""
-"Tugann HEAD in iúl do thagairt neamhbhailí (nó dílleachta).\n"
-"Conair HEAD: '%s'\n"
-"Ábhar HEAD: '%s'"
+msgid "HEAD points to an invalid (or orphaned) reference.\n"
+msgstr "Léiríonn HEAD tagairt neamhbhailí (nó dílleachta).\n"
 
 msgid ""
 "No local or remote refs exist despite at least one remote\n"
@@ -15033,8 +15392,9 @@
 msgid "show extended annotations and reasons, if available"
 msgstr "anótaí agus cúiseanna leathnaithe a thaispeáint, má tá sé ar fáil"
 
-msgid "add 'prunable' annotation to worktrees older than <time>"
-msgstr "cuir anótáil 'prunable' le crainn oibre níos sine ná <time>"
+msgid "add 'prunable' annotation to missing worktrees older than <time>"
+msgstr ""
+"cuir anótáil 'prunable' le crainn oibre atá ar iarraidh atá níos sine ná <am>"
 
 msgid "terminate records with a NUL character"
 msgstr "deireadh a chur le taifid le carachtar NUL"
@@ -15532,6 +15892,9 @@
 msgid "Display help information about Git"
 msgstr "Taispeáin faisnéis chabhrach faoi Git"
 
+msgid "EXPERIMENTAL: Rewrite history"
+msgstr "TURGNAMHACH: Athscríobh an stair"
+
 msgid "Run git hooks"
 msgstr "Rith crúcaí git"
 
@@ -15635,8 +15998,8 @@
 msgid "Pack heads and tags for efficient repository access"
 msgstr "Ceannanna agus clibeanna pacála le haghaidh rochtain éifeachtach"
 
-msgid "Compute unique ID for a patch"
-msgstr "Ríomh ID uathúil le haghaidh paiste"
+msgid "Compute unique IDs for patches"
+msgstr "Ríomh aitheantóirí uathúla le haghaidh paistí"
 
 msgid "Prune all unreachable objects from the object database"
 msgstr ""
@@ -16114,11 +16477,11 @@
 
 #, c-format
 msgid ""
-"attempting to write a commit-graph, but 'commitGraph."
-"changedPathsVersion' (%d) is not supported"
+"attempting to write a commit-graph, but 'commitGraph.changedPathsVersion' "
+"(%d) is not supported"
 msgstr ""
-"ag iarraidh commit-graph a scríobh, ach tá 'commitGraph."
-"changedPathsVersion' (%d) ní thacaítear leis"
+"ag iarraidh commit-graph a scríobh, ach tá 'commitGraph.changedPathsVersion' "
+"(%d) ní thacaítear leis"
 
 msgid "too many commits to write graph"
 msgstr "an iomarca gealltanais graf a scríobh"
@@ -16190,6 +16553,10 @@
 msgstr "ní fhéadfaí a pharsáil a dhéanamh ar thiomantas %s"
 
 #, c-format
+msgid "object %s is a %s, not a %s"
+msgstr "is %s é réad %s, ní %s"
+
+#, c-format
 msgid "%s %s is not a commit!"
 msgstr "Ní gealltanas é %s %s!"
 
@@ -16585,10 +16952,6 @@
 msgstr "droch-luach cumraíochta uimhriúil '%s' do '%s' i %s: %s"
 
 #, c-format
-msgid "bad boolean config value '%s' for '%s'"
-msgstr "droch-luach cumraíochta boolean '%s' do '%s'"
-
-#, c-format
 msgid "failed to expand user dir in: '%s'"
 msgstr "theip ar dir an úsáideora a leathnú i: '%s'"
 
@@ -17270,6 +17633,9 @@
 msgid "unknown value after ws-error-highlight=%.*s"
 msgstr "luach anaithnid tar éis ws-error-highlight =%.*s"
 
+msgid "--find-object requires a git repository"
+msgstr "--find-object teastaíonn stórlann git"
+
 #, c-format
 msgid "unable to resolve '%s'"
 msgstr "ní féidir '%s' a réiteach"
@@ -17650,9 +18016,6 @@
 msgid "<depth>"
 msgstr "<depth>"
 
-msgid "maximum tree depth to recurse"
-msgstr "uasmhéid doimhneacht crainn le hathfhillteach"
-
 msgid "<file>"
 msgstr "<file>"
 
@@ -18008,6 +18371,10 @@
 msgid "git fetch-pack: expected response end packet"
 msgstr "git fetch-pack: paicéad deiridh freagartha ag súil leis"
 
+#, c-format
+msgid "couldn't resolve 'auto' filter '%s': %s"
+msgstr "níorbh fhéidir an scagaire 'uathoibríoch' '%s' a réiteach: %s"
+
 msgid "no matching remote head"
 msgstr "gan ceann iargúlta meaitseála"
 
@@ -18266,8 +18633,8 @@
 "given pattern contains NULL byte (via -f <file>). This is only supported "
 "with -P under PCRE v2"
 msgstr ""
-"tá byte NULL (trí -f<file>) i bpatrún tugtha. Ní thacaítear leis seo ach le -"
-"P faoi PCRE v2"
+"tá byte NULL (trí -f<file>) i bpatrún tugtha. Ní thacaítear leis seo ach le "
+"-P faoi PCRE v2"
 
 #, c-format
 msgid "'%s': unable to read %s"
@@ -18433,8 +18800,20 @@
 msgstr ""
 "Rinneadh neamhaird ar an gcroca '%s' toisc nach bhfuil sé socraithe mar "
 "infheidhmithe.\n"
-"Is féidir leat an rabhadh seo a dhíchumasú le `git config set advice."
-"ignoredHook false `."
+"Is féidir leat an rabhadh seo a dhíchumasú le `git config set "
+"advice.ignoredHook false `."
+
+#, c-format
+msgid "disabled hook '%s' has no command configured"
+msgstr "níl aon ordú cumraithe ag an gcrúca díchumasaithe '%s'"
+
+#, c-format
+msgid ""
+"'hook.%s.command' must be configured or 'hook.%s.event' must be removed; "
+"aborting."
+msgstr ""
+"Caithfear 'hook.%s.command' a chumrú nó 'hook.%s.event' a bhaint; ag cur as "
+"don chóras."
 
 msgid "not a git repository"
 msgstr "ní stór git é"
@@ -18489,6 +18868,23 @@
 
 #, c-format
 msgid ""
+"response requested a delay greater than http.maxRetryTime (%ld > %ld seconds)"
+msgstr ""
+"iarradh moill níos mó ná http.maxRetryTime (%ld > %ld soicind) sa fhreagra"
+
+#, c-format
+msgid ""
+"configured http.retryAfter exceeds http.maxRetryTime (%ld > %ld seconds)"
+msgstr ""
+"tá an http.retryAfter cumraithe níos mó ná http.maxRetryTime (%ld > %ld "
+"soicind)"
+
+#, c-format
+msgid "rate limited, waiting %ld seconds before retry"
+msgstr "ráta teoranta, ag fanacht %ld soicind roimh athiarracht"
+
+#, c-format
+msgid ""
 "number too large to represent as curl_off_t on this platform: %<PRIuMAX>"
 msgstr "uimhir ró-mhór le léiriú mar curl_off_t ar an ardán seo: %<PRIuMAX>"
 
@@ -18569,6 +18965,9 @@
 "socraigh an fillteán sprice le 'git config imap.folder <folder>'. \n"
 "(e.g., 'git config imap.folder Dréachtaí')"
 
+msgid "'auto' filter not supported by this command"
+msgstr "Ní thacaíonn an t-ordú seo le scagaire 'uathoibríoch'"
+
 msgid "expected 'tree:<depth>'"
 msgstr "<depth>'crann ag súil leis: '"
 
@@ -18587,12 +18986,18 @@
 msgid "must escape char in sub-filter-spec: '%c'"
 msgstr "ní mór don char a éalú i sub-filter-spec: '%c'"
 
+msgid "an 'auto' filter cannot be combined"
+msgstr "ní féidir scagaire 'uathoibríoch' a chomhcheangal"
+
 msgid "expected something after combine:"
 msgstr "ag súil le rud éigin tar éis an chomhcheangail:"
 
 msgid "multiple filter-specs cannot be combined"
 msgstr "ní féidir ilshonraíochtaí scagaire a chomhcheangal"
 
+msgid "an 'auto' filter is incompatible with any other filter"
+msgstr "ní bhíonn scagaire 'uathoibríoch' comhoiriúnach le haon scagaire eile"
+
 msgid "unable to upgrade repository format to support partial clone"
 msgstr ""
 "ní féidir formáid an stórais a uasghrádú chun tacú le clónáil pháirteach"
@@ -18624,23 +19029,44 @@
 msgstr "ní féidir crann fréimhe a luchtú le haghaidh an tiomnaidh %s"
 
 #, c-format
+msgid "could not write lock pid file '%s'"
+msgstr "níorbh fhéidir comhad pid glasála '%s' a scríobh"
+
+#, c-format
+msgid "malformed lock pid file '%s'"
+msgstr "comhad pid glasála mífhoirmithe '%s'"
+
+#, c-format
 msgid ""
-"Unable to create '%s.lock': %s.\n"
+"Unable to create '%s': %s.\n"
 "\n"
-"Another git process seems to be running in this repository, e.g.\n"
-"an editor opened by 'git commit'. Please make sure all processes\n"
-"are terminated then try again. If it still fails, a git process\n"
-"may have crashed in this repository earlier:\n"
-"remove the file manually to continue."
 msgstr ""
-"Ní féidir '%s.lock' a chruthú: %s.\n"
+"Ní féidir '%s' a chruthú: %s.\n"
 "\n"
-"Is cosúil go bhfuil próiseas git eile ag rith sa stór seo, e.g.\n"
-"eagarthóir a osclaíodh ag 'git commit'. Déan cinnte le do thoil gach "
-"próiseas\n"
-"foirceanntar ansin déan iarracht arís. Má theipeann air fós, próiseas git\n"
-"d'fhéadfadh go mbeadh sé titim sa stór seo níos luaithe:\n"
-"bain an comhad de láimh chun leanúint ar aghaidh."
+
+#, c-format
+msgid ""
+"Lock may be held by process %<PRIuMAX>; if no git process is running, the "
+"lock file may be stale (PIDs can be reused)"
+msgstr ""
+"B’fhéidir go bhfuil an glas á choinneáil ag an bpróiseas %<PRIuMAX>; mura "
+"bhfuil aon phróiseas git ag rith, b’fhéidir go bhfuil an comhad glasála "
+"seanchaite (is féidir PIDanna a athúsáid)"
+
+#, c-format
+msgid ""
+"Lock was held by process %<PRIuMAX>, which is no longer running; the lock "
+"file appears to be stale"
+msgstr ""
+"Coinníodh an glas ag próiseas %<PRIuMAX>, nach bhfuil ag rith a thuilleadh; "
+"is cosúil go bhfuil an comhad glasála seanchaite"
+
+msgid ""
+"Another git process seems to be running in this repository, or the lock file "
+"may be stale"
+msgstr ""
+"Is cosúil go bhfuil próiseas git eile ag rith sa stór seo, nó b'fhéidir go "
+"bhfuil an comhad glasála seanchaite"
 
 #, c-format
 msgid "Unable to create '%s.lock': %s"
@@ -18952,8 +19378,15 @@
 msgid "malformed line: %s"
 msgstr "líne mhífhoirmithe: %s"
 
-msgid "could not load pack"
-msgstr "ní fhéadfaí pacáiste a luchtú"
+#, c-format
+msgid "could not load pack %d"
+msgstr "níorbh fhéidir pacáiste %d a luchtú"
+
+msgid "too many packs, unable to compact"
+msgstr "an iomarca pacáistí, ní féidir iad a dhlúthú"
+
+msgid "could not determine preferred pack"
+msgstr "ní fhéadfaí pacáiste is fearr a chinneadh"
 
 #, c-format
 msgid "unable to link '%s' to '%s'"
@@ -18963,6 +19396,13 @@
 msgid "failed to clear multi-pack-index at %s"
 msgstr "theip ar innéacs il-phacáiste a ghlanadh ag %s"
 
+#, c-format
+msgid "unknown MIDX version: %d"
+msgstr "leagan MIDX anaithnid: %d"
+
+msgid "cannot perform MIDX compaction with v1 format"
+msgstr "ní féidir dlúthú MIDX a dhéanamh le formáid v1"
+
 msgid "ignoring existing multi-pack-index; checksum mismatch"
 msgstr ""
 "neamhaird a dhéanamh ar innéacs il-phacáiste atá ann cheana; mímheaitseáil"
@@ -19550,10 +19990,6 @@
 msgstr "cineál réad neamhbhailí “%s”"
 
 #, c-format
-msgid "object %s is a %s, not a %s"
-msgstr "is %s é réad %s, ní %s"
-
-#, c-format
 msgid "object %s has unknown type id %d"
 msgstr "réad %s tá cineál ID anaithnid %d aige"
 
@@ -19562,6 +19998,10 @@
 msgstr "ní féidir an réad a pharsáil: %s"
 
 #, c-format
+msgid "unable to open object stream for %s"
+msgstr "ní féidir sruth réada a oscailt do %s"
+
+#, c-format
 msgid "hash mismatch %s"
 msgstr "neamhoiriúnú hash %s"
 
@@ -19577,15 +20017,6 @@
 msgid "%s: ignoring alternate object stores, nesting too deep"
 msgstr "%s: neamhaird a dhéanamh ar stórais rudaí malartacha, neadú ró-"
 
-msgid "unable to fdopen alternates lockfile"
-msgstr "ní féidir comhad glasála malartacha fdopen a úsáid"
-
-msgid "unable to read alternates file"
-msgstr "ní féidir comhad malartach a léamh"
-
-msgid "unable to move new alternates file into place"
-msgstr "ní féidir comhad malartach nua a bhogadh ina áit"
-
 #, c-format
 msgid "path '%s' does not exist"
 msgstr "níl cosán '%s' ann"
@@ -19630,6 +20061,15 @@
 msgid "%s is not a valid '%s' object"
 msgstr "Ní réad bailí '%s' é %s '"
 
+msgid "unable to fdopen alternates lockfile"
+msgstr "ní féidir comhad glasála malartacha fdopen a úsáid"
+
+msgid "unable to read alternates file"
+msgstr "ní féidir comhad malartach a léamh"
+
+msgid "unable to move new alternates file into place"
+msgstr "ní féidir comhad malartach nua a bhogadh ina áit"
+
 #, c-format
 msgid "duplicate entry when writing bitmap index: %s"
 msgstr "iontráil dúblach agus innéacs bitmap á scríobh: %s"
@@ -19848,9 +20288,6 @@
 msgid "multi-pack-index reverse-index chunk is the wrong size"
 msgstr "tá smután droim ar ais-innéacs il-phacáiste ar an méid mícheart"
 
-msgid "could not determine preferred pack"
-msgstr "ní fhéadfaí pacáiste is fearr a chinneadh"
-
 msgid "cannot both write and verify reverse index"
 msgstr "ní féidir innéacs droim a scríobh agus a fhíorú"
 
@@ -19874,6 +20311,10 @@
 msgstr "ní féidir comhaid pacáiste %s a mhapáil %s"
 
 #, c-format
+msgid "could not load .mtimes for cruft pack '%s'"
+msgstr "níorbh fhéidir .mtimes a luchtú don phacáiste cruft '%s'"
+
+#, c-format
 msgid "offset before start of pack index for %s (corrupt index?)"
 msgstr ""
 "fhritháireamh roimh thús an innéacs pacáiste do %s (innéacs truaillithe?)"
@@ -20221,6 +20662,10 @@
 msgid "unable to parse --pretty format"
 msgstr "ní féidir formáid --pretty a pharsáil"
 
+#, c-format
+msgid "%s is not supported by this command"
+msgstr "Ní thacaíonn an t-ordú seo le %s"
+
 msgid "lazy fetching disabled; some objects may not be available"
 msgstr ""
 "tarraingt leisciúil míchumasaithe; b'fhéidir nach mbeidh roinnt rudaí ar fáil"
@@ -20263,6 +20708,22 @@
 msgstr "d’fhógair an freastalaí cianda promisor gan ainm ná URL: %s"
 
 #, c-format
+msgid ""
+"Storing new %s from server for remote '%s'.\n"
+"    '%s' -> '%s'\n"
+msgstr ""
+"Ag stóráil %s nua ón bhfreastalaí don iargúlta '%s'.\n"
+"    '%s' -> '%s'\n"
+
+#, c-format
+msgid "invalid filter '%s' for remote '%s' will not be stored: %s"
+msgstr "ní stórálfar scagaire neamhbhailí '%s' don iargúlta '%s': %s"
+
+#, c-format
+msgid "invalid token '%s' for remote '%s' will not be stored"
+msgstr "ní stórálfar comhartha neamhbhailí '%s' don iargúlta '%s'"
+
+#, c-format
 msgid "unknown '%s' value for '%s' config option"
 msgstr "luach '%s' anaithnid do rogha cumraithe '%s'"
 
@@ -20270,6 +20731,10 @@
 msgid "accepted promisor remote '%s' not found"
 msgstr "nár aimsíodh gealltanas iargúlta '%s'"
 
+#, c-format
+msgid "promisor remote '%s' advertised invalid filter '%s': %s"
+msgstr "fógraíodh scagaire neamhbhailí '%s' ag an ngealltóir iargúlta '%s': %s"
+
 msgid "object-info: expected flush after arguments"
 msgstr "ear-eolas: súil le sruth tar éis argóintí"
 
@@ -20530,6 +20995,14 @@
 msgstr "%s: ní féidir titim go dtí céim #0"
 
 #, c-format
+msgid ""
+"Skipping submodule due to ignore=all: %s\n"
+"Use --force if you really want to add the submodule."
+msgstr ""
+"Ag scipeáil an fho-mhodúil mar gheall ar ignore=all: %s\n"
+"Úsáid --force más mian leat an fo-mhodúl a chur leis i ndáiríre."
+
+#, c-format
 msgid "unexpected diff status %c"
 msgstr "stádas diff gan choinne %c"
 
@@ -20911,6 +21384,12 @@
 msgid "no reflog for '%s'"
 msgstr "gan aon athbhreithniú do '%s'"
 
+#, c-format
+msgid "in '%s' phase, update aborted by the reference-transaction hook"
+msgstr ""
+"i gcéim '%s', cuireadh deireadh leis an nuashonrú ag an crúca idirbhirt "
+"tagartha"
+
 msgid "Checking references consistency"
 msgstr "Comhsheasmhacht tagairtí"
 
@@ -21023,9 +21502,6 @@
 msgid "ref updates forbidden inside quarantine environment"
 msgstr "nuashonruithe ref toirmiscthe laistigh de"
 
-msgid "ref updates aborted by hook"
-msgstr "nuashonruithe tagartha a chuirtear deireadh leis"
-
 #, c-format
 msgid "'%s' exists; cannot create '%s'"
 msgstr "Tá '%s' ann; ní féidir '%s' a chruthú"
@@ -21229,6 +21705,14 @@
 msgstr "ní féidir rochtain a fháil ar '%s' le cumraíocht http.pinnedPubkey: %s"
 
 #, c-format
+msgid "rate limited by '%s', please try again in %ld seconds"
+msgstr "ráta teoranta ag '%s', déan iarracht arís i %ld soicind"
+
+#, c-format
+msgid "rate limited by '%s', please try again later"
+msgstr "ráta teoranta ag '%s', déan iarracht arís ar ball"
+
+#, c-format
 msgid "unable to access '%s': %s"
 msgstr "ní féidir teacht ar '%s': %s"
 
@@ -21514,11 +21998,12 @@
 msgstr "* Ag neamhaird den tagairt ghreannmhar '%s' go háitiúil"
 
 #, c-format
-msgid "Your branch is based on '%s', but the upstream is gone.\n"
-msgstr "Tá do bhrainse bunaithe ar '%s', ach tá an suas sruth imithe.\n"
-
-msgid "  (use \"git branch --unset-upstream\" to fixup)\n"
-msgstr " (bain úsáid as \"git branch --unset-upstream\" chun é a dheisiú)\n"
+msgid ""
+"ignoring value '%s' for status.compareBranches, only @{upstream} and @{push} "
+"are supported"
+msgstr ""
+"ag neamhaird luach '%s' le haghaidh status.compareBranches, ní thacaítear "
+"ach le @{upstream} agus @{push}"
 
 #, c-format
 msgid "Your branch is up to date with '%s'.\n"
@@ -21583,6 +22068,13 @@
 "le leatsa)\n"
 
 #, c-format
+msgid "Your branch is based on '%s', but the upstream is gone.\n"
+msgstr "Tá do bhrainse bunaithe ar '%s', ach tá an suas sruth imithe.\n"
+
+msgid "  (use \"git branch --unset-upstream\" to fixup)\n"
+msgstr " (bain úsáid as \"git branch --unset-upstream\" chun é a dheisiú)\n"
+
+#, c-format
 msgid "cannot parse expected object name '%s'"
 msgstr "ní féidir ainm réad a bhfuil súil leis '%s' a pharsáil"
 
@@ -21660,6 +22152,38 @@
 msgid "replace depth too high for object %s"
 msgstr "doimhneacht ró-ard a chur in ionad do réad %s"
 
+#, c-format
+msgid "'%s' is not a valid commit-ish for %s"
+msgstr "Ní comhartha bailí commit-ish é '%s' do %s"
+
+#, c-format
+msgid "argument to %s must be a reference"
+msgstr "ní mór don argóint chuig %s a bheith ina thagairt"
+
+#, c-format
+msgid ""
+"'%s' cannot be used with multiple revision ranges because the ordering would "
+"be ill-defined"
+msgstr ""
+"Ní féidir '%s' a úsáid le raonta athbhreithnithe iolracha mar go mbeadh an t-"
+"ordú mícheart"
+
+msgid "need some commits to replay"
+msgstr "teastaíonn roinnt gealltanais chun athsheinm"
+
+msgid "all positive revisions given must be references"
+msgstr "caithfidh gach athbhreithniú dearfach a thugtar a bheith ina"
+
+msgid "'--ref' cannot be used with multiple revision ranges"
+msgstr "Ní féidir '--ref' a úsáid le raonta athbhreithnithe iolracha"
+
+#, c-format
+msgid "'%s' is not a valid refname"
+msgstr "Ní athainm bailí é '%s'"
+
+msgid "compatibility hash algorithm support requires Rust"
+msgstr "éilíonn tacaíocht algartam hais comhoiriúnachta Rust"
+
 msgid "corrupt MERGE_RR"
 msgstr "truaillithe MERGE_RR"
 
@@ -22397,10 +22921,6 @@
 msgstr "Ní lipéad bailí é '%s'"
 
 #, c-format
-msgid "'%s' is not a valid refname"
-msgstr "Ní athainm bailí é '%s'"
-
-#, c-format
 msgid "update-ref requires a fully qualified refname e.g. refs/heads/%s"
 msgstr "teastaíonn athainm iomlán cáilithe uasghabháilte e.g. refs/heads/%s"
 
@@ -22492,6 +23012,15 @@
 msgid "cannot revert during a cherry-pick."
 msgstr "ní féidir filleadh ar ais le linn pioc silíní."
 
+msgid "trailers file contains empty line"
+msgstr "tá líne folamh i gcomhad na leantóirí"
+
+msgid "trailers file is empty"
+msgstr "tá comhad na leantóirí folamh"
+
+msgid "cannot read trailers files"
+msgstr "ní féidir comhaid leantóirí a léamh"
+
 msgid "unusable squash-onto"
 msgstr "squash-on neamhúsáidte"
 
@@ -22983,6 +23512,14 @@
 "\n"
 "git config --global --add safe.directory %s"
 
+#, c-format
+msgid "error reading '%s'"
+msgstr "earráid ag léamh '%s'"
+
+#, c-format
+msgid "not a regular file: '%s'"
+msgstr "ní comhad rialta é: '%s'"
+
 msgid "Unable to read current working directory"
 msgstr "Ní féidir eolaire oibre reatha a léamh"
 
@@ -23008,6 +23545,10 @@
 msgstr "ní féidir stór lom '%s' a úsáid (safe.bareRepository is '%s')"
 
 #, c-format
+msgid "unknown ref storage format: '%s'"
+msgstr "formáid stórála tagartha anaithnid: '%s'"
+
+#, c-format
 msgid ""
 "problem with core.sharedRepository filemode value (0%.3o).\n"
 "The owner of files must always have read and write permissions."
@@ -23351,10 +23892,6 @@
 msgstr "ní fhéadfaí ainm a lorg don fho-mhodúl '%s'"
 
 #, c-format
-msgid "refusing to move '%s' into an existing git dir"
-msgstr "diúltú '%s' a bhogadh isteach i git dir atá ann cheana"
-
-#, c-format
 msgid ""
 "Migrating git directory of '%s%s' from\n"
 "'%s' to\n"
@@ -23372,6 +23909,29 @@
 msgstr "d’fhill an crann ls cód fillte gan choinne %d"
 
 #, c-format
+msgid ""
+"the 'submodule.%s.gitdir' config does not exist for module '%s'. Please "
+"ensure it is set, for example by running something like: 'git config "
+"submodule.%s.gitdir .git/modules/%s'. For details see the "
+"extensions.submodulePathConfig documentation."
+msgstr ""
+"níl an chumraíocht 'submodule.%s.gitdir' ann don mhodúl '%s'. Cinntigh go "
+"bhfuil sé socraithe, mar shampla trí rud éigin cosúil le: 'git config "
+"submodule.%s.gitdir .git/modules/%s' a rith. Le haghaidh tuilleadh sonraí, "
+"féach ar an doiciméadú extensions.submodulePathConfig."
+
+msgid ""
+"enabling extensions.submodulePathConfig might fix the following error, if "
+"it's not already enabled."
+msgstr ""
+"d’fhéadfadh sé go réiteodh cumasú extensions.submodulePathConfig an earráid "
+"seo a leanas, mura bhfuil sé cumasaithe cheana féin."
+
+#, c-format
+msgid "refusing to create/use '%s' in another submodule's  git dir."
+msgstr "ag diúltú '%s' a chruthú/a úsáid i ndoiciméad git fo-mhodúil eile."
+
+#, c-format
 msgid "failed to lstat '%s'"
 msgstr "theip ar lstat '%s'"
 
@@ -23425,6 +23985,10 @@
 msgid "too many commits marked reachable"
 msgstr "an iomarca gealltanais atá marcáilte inrochtana"
 
+#, c-format
+msgid "could not find MIDX with checksum %s"
+msgstr "níorbh fhéidir MIDX a aimsiú le suim seiceála %s"
+
 msgid "could not determine MIDX preferred pack"
 msgstr "ní fhéadfaí pacáiste is fearr MIDX a chinneadh"
 
@@ -23517,6 +24081,28 @@
 msgid "empty trailer token in trailer '%.*s'"
 msgstr "comhartha leantóra folamh i leantóir '%.*s'"
 
+msgid "empty --trailer argument"
+msgstr "argóint --trailer folamh"
+
+#, c-format
+msgid "invalid trailer '%s': missing key before separator"
+msgstr "leantóir neamhbhailí '%s': eochair ar iarraidh roimh an deighilteoir"
+
+#, c-format
+msgid "could not stat %s"
+msgstr "ní raibh ann %s a shástáil"
+
+#, c-format
+msgid "file %s is not a regular file"
+msgstr "ní comhad rialta é comhad %s"
+
+#, c-format
+msgid "file %s is not writable by user"
+msgstr "ní féidir an t-úsáideoir comhad %s a scríobh"
+
+msgid "could not write to temporary file"
+msgstr "níorbh fhéidir scríobh chuig comhad sealadach"
+
 msgid "full write to remote helper failed"
 msgstr "theip ar scríobh iomlán chuig cianchúntóir"
 
@@ -24890,8 +25476,14 @@
 "Is iad na comhaid seo a leanas 8bit, ach ná dearbhaíonn siad Ionchódú "
 "Aistrithe Ábhar.\n"
 
-msgid "Which 8bit encoding should I declare [UTF-8]? "
-msgstr "Cén ionchódú 8 giotán ba chóir dom a dhearbhú [UTF-8]? "
+msgid "Declare which 8bit encoding to use [default: UTF-8]? "
+msgstr "Dearbhaigh cén ionchódú 8 giotán atá le húsáid [réamhshocrú: UTF-8]? "
+
+#, perl-format
+msgid "'%s' does not appear to be a valid charset name. Use it anyway [y/N]? "
+msgstr ""
+"Ní cosúil gur ainm bailí tacair charachtair é '%s'. An bhfuil tú ag iarraidh "
+"é a úsáid ar aon nós [tá/ní]? "
 
 #, perl-format
 msgid ""
@@ -24931,6 +25523,10 @@
 msgid "CA path \"%s\" does not exist"
 msgstr "Níl cosán CA “%s” ann"
 
+#, perl-format
+msgid "Only client key \"%s\" specified"
+msgstr "Eochair chliaint \"%s\" amháin sonraithe"
+
 msgid ""
 "    The Cc list above has been expanded by additional\n"
 "    addresses found in the patch commit message. By default\n"
@@ -25095,186 +25691,3 @@
 #, perl-format
 msgid "Do you really want to send %s? [y|N]: "
 msgstr "An bhfuil tú ag iarraidh %s a sheoladh i ndáiríre? [y|N]: "
-
-#, c-format
-#~ msgid "could not resolve %s"
-#~ msgstr "ní fhéadfaí %s a réiteach"
-
-#, c-format
-#~ msgid "Checking %s link"
-#~ msgstr "Nasc %s a sheiceáil"
-
-#, c-format
-#~ msgid "%s points to something strange (%s)"
-#~ msgstr "Léiríonn %s rud éigin aisteach (%s)"
-
-#, c-format
-#~ msgid "%s: detached HEAD points at nothing"
-#~ msgstr "%s: pointí HEAD scoite ag aon rud"
-
-#, c-format
-#~ msgid "notice: %s points to an unborn branch (%s)"
-#~ msgstr "fógra: Díríonn %s chuig brainse neamhbhreithe (%s)"
-
-#, c-format
-#~ msgid "%s: object missing"
-#~ msgstr "%s: réad ar iarraidh"
-
-#~ msgid ""
-#~ "cannot implicitly determine whether this is an --advance or --onto "
-#~ "operation"
-#~ msgstr ""
-#~ "ní féidir a chinneadh go hinneach an oibríocht --advance nó --onto é seo"
-
-#~ msgid ""
-#~ "cannot advance target with multiple source branches because ordering "
-#~ "would be ill-defined"
-#~ msgstr ""
-#~ "ní féidir leis an sprioc a chur chun cinn le brainsí foinse iolracha mar "
-#~ "go mbeadh ordú"
-
-#~ msgid "cannot implicitly determine correct base for --onto"
-#~ msgstr "ní féidir leis an mbonn ceart do --onto a chinneadh go hinneach"
-
-#~ msgid "advance all branches contained in revision-range"
-#~ msgstr "gach brainse atá sa raon athbhreithnithe a chur chun cinn"
-
-#~ msgid "Reset current HEAD to the specified state"
-#~ msgstr "Athshocraigh HEAD reatha go dtí an stát sonraithe"
-
-#, c-format
-#~ msgid "%u.%2.2u GiB/s"
-#~ msgstr "%u.%2.2u GiB/s"
-
-#, c-format
-#~ msgid "%u.%2.2u MiB"
-#~ msgstr "%u.%2.2u MiB"
-
-#, c-format
-#~ msgid "%u.%2.2u MiB/s"
-#~ msgstr "%u.%2.2u MiB/s"
-
-#, c-format
-#~ msgid "%u.%2.2u KiB"
-#~ msgstr "%u.%2.2u KiB"
-
-#, c-format
-#~ msgid "%u.%2.2u KiB/s"
-#~ msgstr "%u.%2.2u KiB/s"
-
-#, c-format
-#~ msgid "%u byte"
-#~ msgid_plural "%u bytes"
-#~ msgstr[0] "%u beart"
-#~ msgstr[1] "%u beart"
-#~ msgstr[2] "%u beart"
-
-#, c-format
-#~ msgid "%u byte/s"
-#~ msgid_plural "%u bytes/s"
-#~ msgstr[0] "%u beart/s"
-#~ msgstr[1] "%u beart/s"
-#~ msgstr[2] "%u beart/s"
-
-#~ msgid "No previous hunk"
-#~ msgstr "Níl aon hunk roimhe seo"
-
-#~ msgid "No next hunk"
-#~ msgstr "Níl aon chéad hunk eile"
-
-#, c-format
-#~ msgid "   (%s will become dangling)"
-#~ msgstr "   (beidh %s ag crochadh)"
-
-#, c-format
-#~ msgid "   (%s has become dangling)"
-#~ msgstr "   (%s has become dangling)"
-
-#~ msgid "git for-each-ref [<options>] [<pattern>]"
-#~ msgstr "git for-each-ref [<options>] [<pattern>]"
-
-#~ msgid "git for-each-ref [--merged [<commit>]] [--no-merged [<commit>]]"
-#~ msgstr "git for-each-ref [--merged [<commit>]] [--no-merged [<commit>]]"
-
-#~ msgid "git for-each-ref [--contains [<commit>]] [--no-contains [<commit>]]"
-#~ msgstr "git for-each-ref [--contains [<commit>]] [--no-contains [<commit>]]"
-
-#~ msgid "use at most one of --auto and --schedule=<frequency>"
-#~ msgstr "bain úsáid as --auto agus --schedule=<frequency> ar a mhéad"
-
-#, c-format
-#~ msgid "Final output: %d %s\n"
-#~ msgstr "Aschur deiridh: %d %s\n"
-
-#, c-format
-#~ msgid "%d (FSCK_IGNORE?) should never trigger this callback"
-#~ msgstr "%d (FSCK_IGNORE?) níor cheart go spreagfadh an t-aisghlaoch seo"
-
-#~ msgid ""
-#~ "git pack-objects --stdout [<options>] [< <ref-list> | < <object-list>]"
-#~ msgstr ""
-#~ "git pack-objects --stdout [<options>] [< <ref-list> | < <object-list>]"
-
-#~ msgid ""
-#~ "git pack-objects [<options>] <base-name> [< <ref-list> | < <object-list>]"
-#~ msgstr ""
-#~ "git pack-objects [<options>] <base-name> [< <ref-list> | < <object-list>]"
-
-#~ msgid "cannot use --stdin-packs with --cruft"
-#~ msgstr "ní féidir --stdin-packs a úsáid le --cruft"
-
-#~ msgid ""
-#~ "'git pack-redundant' is nominated for removal.\n"
-#~ "If you still use this command, please add an extra\n"
-#~ "option, '--i-still-use-this', on the command line\n"
-#~ "and let us know you still use it by sending an e-mail\n"
-#~ "to <git@vger.kernel.org>.  Thanks.\n"
-#~ msgstr ""
-#~ "Tá 'git pack-redundant' ainmnithe le baint.\n"
-#~ "Má úsáideann tú an t-ordú seo fós, cuir rogha b\n"
-#~ "hreise, '--i-still-use-this', leis an líne ordaithe \n"
-#~ "agus cuir in iúl dúinn go n-úsáideann tú fós é trí ríomhphost \n"
-#~ "a sheoladh chuig <git@vger.kernel.org>.  Go raibh maith agat.\n"
-
-#~ msgid ""
-#~ "git pack-refs [--all] [--no-prune] [--auto] [--include <pattern>] [--"
-#~ "exclude <pattern>]"
-#~ msgstr ""
-#~ "<pattern><pattern>git pack-refs [--all] [--no-prunes] [--auto] [--"
-#~ "include] [--eisiamh]"
-
-#, c-format
-#~ msgid "deleting '%s' failed"
-#~ msgstr "theip ar '%s' a scriosadh"
-
-#, c-format
-#~ msgid "creating '%s' failed"
-#~ msgstr "theip ar chruthú '%s'"
-
-#, c-format
-#~ msgid "unreachable: invalid reference: %s"
-#~ msgstr "unrochtana: tagairt neamhbhailí: %s"
-
-#, c-format
-#~ msgid "could not open index for %s"
-#~ msgstr "ní raibh in ann innéacs a oscailt do %s"
-
-#~ msgid "trying to write commit not in index"
-#~ msgstr "ag iarraidh tiomantas a scríobh ní in innéacs"
-
-#~ msgid "cannot handle pushes this big"
-#~ msgstr "ní féidir brú mór seo a láimhseáil"
-
-#~ msgid "git cat-file (-t | -s) [--allow-unknown-type] <object>"
-#~ msgstr "git cat-file (-t | -s) [--leis-cineál gan eolas] <object>"
-
-#~ msgid "allow -s and -t to work with broken/corrupt objects"
-#~ msgstr "ligean do -s agus -t oibriú le rudaí briste/truaillithe"
-
-#, c-format
-#~ msgid "%s: object is of unknown type '%s': %s"
-#~ msgstr "%s: tá réad de chineál anaithnid '%s': %s"
-
-#, c-format
-#~ msgid "unable to read '%s'"
-#~ msgstr "ní féidir '%s' a léamh"
diff --git a/po/ru.po b/po/ru.po
index 3e56eb5..e8845ca 100644
--- a/po/ru.po
+++ b/po/ru.po
@@ -369,8 +369,8 @@
 #, c-format
 msgid "Discard mode change from index and worktree [y,n,q,a,d%s,?]? "
 msgstr ""
-"Отменить изменения режима доступа в индексе и рабочем каталоге [y,n,q,a,"
-"d%s,?]? "
+"Отменить изменения режима доступа в индексе и рабочем каталоге "
+"[y,n,q,a,d%s,?]? "
 
 #, c-format
 msgid "Discard deletion from index and worktree [y,n,q,a,d%s,?]? "
@@ -400,8 +400,8 @@
 #, c-format
 msgid "Apply mode change to index and worktree [y,n,q,a,d%s,?]? "
 msgstr ""
-"Применить изменения режима доступа к индексу и рабочему каталогу [y,n,q,a,"
-"d%s,?]? "
+"Применить изменения режима доступа к индексу и рабочему каталогу "
+"[y,n,q,a,d%s,?]? "
 
 #, c-format
 msgid "Apply deletion to index and worktree [y,n,q,a,d%s,?]? "
@@ -2966,8 +2966,8 @@
 msgstr "HEAD не найден в refs/heads!"
 
 msgid ""
-"branch with --recurse-submodules can only be used if submodule."
-"propagateBranches is enabled"
+"branch with --recurse-submodules can only be used if "
+"submodule.propagateBranches is enabled"
 msgstr ""
 
 msgid "--recurse-submodules can only be used to create branches"
@@ -3997,8 +3997,8 @@
 "clean.requireForce defaults to true and neither -i, -n, nor -f given; "
 "refusing to clean"
 msgstr ""
-"clean.requireForce установлен по умолчанию как true и ни одна из опций -i, -"
-"n или -f не указана; отказ очистки"
+"clean.requireForce установлен по умолчанию как true и ни одна из опций -i, "
+"-n или -f не указана; отказ очистки"
 
 msgid "-x and -X cannot be used together"
 msgstr "нельзя использовать одновременно -x и -X"
@@ -5890,8 +5890,8 @@
 msgstr ""
 
 msgid ""
-"--filter can only be used with the remote configured in extensions."
-"partialclone"
+"--filter can only be used with the remote configured in "
+"extensions.partialclone"
 msgstr ""
 
 msgid "--atomic can only be used when fetching from one remote"
@@ -8385,8 +8385,8 @@
 
 msgid "Please stage your changes to .gitmodules or stash them to proceed"
 msgstr ""
-"Чтобы продолжить, проиндексируйте или спрячьте ваши изменения в файле ."
-"gitmodules"
+"Чтобы продолжить, проиндексируйте или спрячьте ваши изменения в "
+"файле .gitmodules"
 
 #, c-format
 msgid "%.*s is in index"
@@ -16134,8 +16134,8 @@
 msgstr ""
 "Перехватчик «%s» был проигнорирован, так как он не установлен как "
 "исполняемый.\n"
-"Вы можете отключить это предупреждение с помощью команды «git config advice."
-"ignoredHook false»."
+"Вы можете отключить это предупреждение с помощью команды «git config "
+"advice.ignoredHook false»."
 
 #, c-format
 msgid "argument to --packfile must be a valid hash (got '%s')"
diff --git a/po/sv.po b/po/sv.po
index 7e2e441..8abda2e 100644
--- a/po/sv.po
+++ b/po/sv.po
@@ -5,10 +5,10 @@
 #
 msgid ""
 msgstr ""
-"Project-Id-Version: git 2.53.0\n"
+"Project-Id-Version: git 2.54.0\n"
 "Report-Msgid-Bugs-To: Git Mailing List <git@vger.kernel.org>\n"
-"POT-Creation-Date: 2026-01-22 23:57+0000\n"
-"PO-Revision-Date: 2026-01-27 19:33+0100\n"
+"POT-Creation-Date: 2026-04-16 12:26+0100\n"
+"PO-Revision-Date: 2026-04-16 13:29+0100\n"
 "Last-Translator: Peter Krefting <peter@softwolves.pp.se>\n"
 "Language-Team: Svenska <tp-sv@listor.tp-sv.se>\n"
 "Language: sv\n"
@@ -18,10 +18,6 @@
 "Plural-Forms: nplurals=2; plural=(n != 1);\n"
 
 #, c-format
-msgid "%s cannot be negative"
-msgstr "%s kan inte vara negativt"
-
-#, c-format
 msgid "Huh (%s)?"
 msgstr "Vadå (%s)?"
 
@@ -65,7 +61,7 @@
 msgstr "Återställ"
 
 msgid "Could not parse HEAD^{tree}"
-msgstr "kunde inte tolka HEAD^{tree}"
+msgstr "Kunde inte tolka HEAD^{tree}"
 
 #, c-format
 msgid "reverted %d path\n"
@@ -175,20 +171,20 @@
 msgstr "Hej då.\n"
 
 #, c-format
-msgid "Stage mode change [y,n,q,a,d%s,?]? "
-msgstr "Köa ändrat läge [y,n,q,a,d%s,?]? "
+msgid "Stage mode change%s [y,n,q,a,d%s,?]? "
+msgstr "Köa ändrat läge%s [y,n,q,a,d%s,?]? "
 
 #, c-format
-msgid "Stage deletion [y,n,q,a,d%s,?]? "
-msgstr "Köa borttagning [y,n,q,a,d%s,?]? "
+msgid "Stage deletion%s [y,n,q,a,d%s,?]? "
+msgstr "Köa borttagning%s [y,n,q,a,d%s,?]? "
 
 #, c-format
-msgid "Stage addition [y,n,q,a,d%s,?]? "
-msgstr "Köa tillägg [y,n,q,a,d%s,?]? "
+msgid "Stage addition%s [y,n,q,a,d%s,?]? "
+msgstr "Köa tillägg%s [y,n,q,a,d%s,?]? "
 
 #, c-format
-msgid "Stage this hunk [y,n,q,a,d%s,?]? "
-msgstr "Köa stycket [y,n,q,a,d%s,?]? "
+msgid "Stage this hunk%s [y,n,q,a,d%s,?]? "
+msgstr "Köa stycket%s [y,n,q,a,d%s,?]? "
 
 msgid ""
 "If the patch applies cleanly, the edited hunk will immediately be marked for "
@@ -211,20 +207,20 @@
 "d - köa inte stycket eller något av de följande i filen\n"
 
 #, c-format
-msgid "Stash mode change [y,n,q,a,d%s,?]? "
-msgstr "Stash:a ändrat läge [y,n,q,a,d%s,?]? "
+msgid "Stash mode change%s [y,n,q,a,d%s,?]? "
+msgstr "Stash:a ändrat läge%s [y,n,q,a,d%s,?]? "
 
 #, c-format
-msgid "Stash deletion [y,n,q,a,d%s,?]? "
-msgstr "Stash:a borttagning [y,n,q,a,d%s,?]? "
+msgid "Stash deletion%s [y,n,q,a,d%s,?]? "
+msgstr "Stash:a borttagning%s [y,n,q,a,d%s,?]? "
 
 #, c-format
-msgid "Stash addition [y,n,q,a,d%s,?]? "
-msgstr "Stash:a tillägg [y,n,q,a,d%s,?]? "
+msgid "Stash addition%s [y,n,q,a,d%s,?]? "
+msgstr "Stash:a tillägg%s [y,n,q,a,d%s,?]? "
 
 #, c-format
-msgid "Stash this hunk [y,n,q,a,d%s,?]? "
-msgstr "Stash:a stycket [y,n,q,a,d%s,?]? "
+msgid "Stash this hunk%s [y,n,q,a,d%s,?]? "
+msgstr "Stash:a stycket%s [y,n,q,a,d%s,?]? "
 
 msgid ""
 "If the patch applies cleanly, the edited hunk will immediately be marked for "
@@ -247,20 +243,20 @@
 "d - ”stash”:a inte stycket eller något av de följande i filen\n"
 
 #, c-format
-msgid "Unstage mode change [y,n,q,a,d%s,?]? "
-msgstr "Ta bort ändrat läge från kön [y,n,q,a,d%s,?]? "
+msgid "Unstage mode change%s [y,n,q,a,d%s,?]? "
+msgstr "Ta bort ändrat läge från kön%s [y,n,q,a,d%s,?]? "
 
 #, c-format
-msgid "Unstage deletion [y,n,q,a,d%s,?]? "
-msgstr "Ta bort borttagning från kön [y,n,q,a,d%s,?]? "
+msgid "Unstage deletion%s [y,n,q,a,d%s,?]? "
+msgstr "Ta bort borttagning från kön%s [y,n,q,a,d%s,?]? "
 
 #, c-format
-msgid "Unstage addition [y,n,q,a,d%s,?]? "
-msgstr "Ta bort tillägg från kön [y,n,q,a,d%s,?]? "
+msgid "Unstage addition%s [y,n,q,a,d%s,?]? "
+msgstr "Ta bort tillägg från kön%s [y,n,q,a,d%s,?]? "
 
 #, c-format
-msgid "Unstage this hunk [y,n,q,a,d%s,?]? "
-msgstr "Ta bort stycket från kön [y,n,q,a,d%s,?]? "
+msgid "Unstage this hunk%s [y,n,q,a,d%s,?]? "
+msgstr "Ta bort stycket från kön%s [y,n,q,a,d%s,?]? "
 
 msgid ""
 "If the patch applies cleanly, the edited hunk will immediately be marked for "
@@ -283,20 +279,20 @@
 "d - ta inte bort stycket eller något av de följande i filen från kön\n"
 
 #, c-format
-msgid "Apply mode change to index [y,n,q,a,d%s,?]? "
-msgstr "Applicera ändrat läge på indexet [y,n,q,a,d%s,?]? "
+msgid "Apply mode change to index%s [y,n,q,a,d%s,?]? "
+msgstr "Applicera ändrat läge på indexet%s [y,n,q,a,d%s,?]? "
 
 #, c-format
-msgid "Apply deletion to index [y,n,q,a,d%s,?]? "
-msgstr "Applicera borttagning på indexet [y,n,q,a,d%s,?]? "
+msgid "Apply deletion to index%s [y,n,q,a,d%s,?]? "
+msgstr "Applicera borttagning på indexet%s [y,n,q,a,d%s,?]? "
 
 #, c-format
-msgid "Apply addition to index [y,n,q,a,d%s,?]? "
-msgstr "Applicera tillägg på indexet [y,n,q,a,d%s,?]? "
+msgid "Apply addition to index%s [y,n,q,a,d%s,?]? "
+msgstr "Applicera tillägg på indexet%s [y,n,q,a,d%s,?]? "
 
 #, c-format
-msgid "Apply this hunk to index [y,n,q,a,d%s,?]? "
-msgstr "Applicera stycket på indexet [y,n,q,a,d%s,?]? "
+msgid "Apply this hunk to index%s [y,n,q,a,d%s,?]? "
+msgstr "Applicera stycket på indexet%s [y,n,q,a,d%s,?]? "
 
 msgid ""
 "If the patch applies cleanly, the edited hunk will immediately be marked for "
@@ -319,20 +315,20 @@
 "d - applicera inte stycket eller något av de följande i filen\n"
 
 #, c-format
-msgid "Discard mode change from worktree [y,n,q,a,d%s,?]? "
-msgstr "Kasta ändrat läge från arbetskatalogen [y,n,q,a,d%s,?]? "
+msgid "Discard mode change from worktree%s [y,n,q,a,d%s,?]? "
+msgstr "Kasta ändrat läge från arbetskatalogen%s [y,n,q,a,d%s,?]? "
 
 #, c-format
-msgid "Discard deletion from worktree [y,n,q,a,d%s,?]? "
-msgstr "Kasta borttagning från arbetskatalogen [y,n,q,a,d%s,?]? "
+msgid "Discard deletion from worktree%s [y,n,q,a,d%s,?]? "
+msgstr "Kasta borttagning från arbetskatalogen%s [y,n,q,a,d%s,?]? "
 
 #, c-format
-msgid "Discard addition from worktree [y,n,q,a,d%s,?]? "
-msgstr "Kasta tillägg från arbetskatalogen [y,n,q,a,d%s,?]? "
+msgid "Discard addition from worktree%s [y,n,q,a,d%s,?]? "
+msgstr "Kasta tillägg från arbetskatalogen%s [y,n,q,a,d%s,?]? "
 
 #, c-format
-msgid "Discard this hunk from worktree [y,n,q,a,d%s,?]? "
-msgstr "Kasta stycket från arbetskatalogen [y,n,q,a,d%s,?]? "
+msgid "Discard this hunk from worktree%s [y,n,q,a,d%s,?]? "
+msgstr "Kasta stycket från arbetskatalogen%s [y,n,q,a,d%s,?]? "
 
 msgid ""
 "If the patch applies cleanly, the edited hunk will immediately be marked for "
@@ -355,20 +351,20 @@
 "d - förkasta inte stycket eller något av de följande i filen\n"
 
 #, c-format
-msgid "Discard mode change from index and worktree [y,n,q,a,d%s,?]? "
-msgstr "Kasta ändrat läge från indexet och arbetskatalogen [y,n,q,a,d%s,?]? "
+msgid "Discard mode change from index and worktree%s [y,n,q,a,d%s,?]? "
+msgstr "Kasta ändrat läge från indexet och arbetskatalogen%s [y,n,q,a,d%s,?]? "
 
 #, c-format
-msgid "Discard deletion from index and worktree [y,n,q,a,d%s,?]? "
-msgstr "Kasta borttagning från indexet och arbetskatalogen [y,n,q,a,d%s,?]? "
+msgid "Discard deletion from index and worktree%s [y,n,q,a,d%s,?]? "
+msgstr "Kasta borttagning från indexet och arbetskatalogen%s [y,n,q,a,d%s,?]? "
 
 #, c-format
-msgid "Discard addition from index and worktree [y,n,q,a,d%s,?]? "
-msgstr "Kasta tillägg från indexet och arbetskatalogen [y,n,q,a,d%s,?]? "
+msgid "Discard addition from index and worktree%s [y,n,q,a,d%s,?]? "
+msgstr "Kasta tillägg från indexet och arbetskatalogen%s [y,n,q,a,d%s,?]? "
 
 #, c-format
-msgid "Discard this hunk from index and worktree [y,n,q,a,d%s,?]? "
-msgstr "Kasta stycket från indexet och arbetskatalogen [y,n,q,a,d%s,?]? "
+msgid "Discard this hunk from index and worktree%s [y,n,q,a,d%s,?]? "
+msgstr "Kasta stycket från indexet och arbetskatalogen%s [y,n,q,a,d%s,?]? "
 
 msgid ""
 "y - discard this hunk from index and worktree\n"
@@ -384,20 +380,22 @@
 "d - förkasta inte stycket eller något av de följande i filen\n"
 
 #, c-format
-msgid "Apply mode change to index and worktree [y,n,q,a,d%s,?]? "
-msgstr "Applicera ändrat läge på indexet och arbetskatalogen [y,n,q,a,d%s,?]? "
+msgid "Apply mode change to index and worktree%s [y,n,q,a,d%s,?]? "
+msgstr ""
+"Applicera ändrat läge på indexet och arbetskatalogen%s [y,n,q,a,d%s,?]? "
 
 #, c-format
-msgid "Apply deletion to index and worktree [y,n,q,a,d%s,?]? "
-msgstr "Applicera borttagning på indexet och arbetskatalogen [y,n,q,a,d%s,?]? "
+msgid "Apply deletion to index and worktree%s [y,n,q,a,d%s,?]? "
+msgstr ""
+"Applicera borttagning på indexet och arbetskatalogen%s [y,n,q,a,d%s,?]? "
 
 #, c-format
-msgid "Apply addition to index and worktree [y,n,q,a,d%s,?]? "
-msgstr "Applicera tillägg på indexet och arbetskatalogen [y,n,q,a,d%s,?]? "
+msgid "Apply addition to index and worktree%s [y,n,q,a,d%s,?]? "
+msgstr "Applicera tillägg på indexet och arbetskatalogen%s [y,n,q,a,d%s,?]? "
 
 #, c-format
-msgid "Apply this hunk to index and worktree [y,n,q,a,d%s,?]? "
-msgstr "Applicera stycket på indexet och arbetskatalogen [y,n,q,a,d%s,?]? "
+msgid "Apply this hunk to index and worktree%s [y,n,q,a,d%s,?]? "
+msgstr "Applicera stycket på indexet och arbetskatalogen%s [y,n,q,a,d%s,?]? "
 
 msgid ""
 "y - apply this hunk to index and worktree\n"
@@ -413,20 +411,20 @@
 "d - applicera inte stycket eller något av de följande i filen\n"
 
 #, c-format
-msgid "Apply mode change to worktree [y,n,q,a,d%s,?]? "
-msgstr "Applicera ändrat läge på arbetskatalogen [y,n,q,a,d%s,?]? "
+msgid "Apply mode change to worktree%s [y,n,q,a,d%s,?]? "
+msgstr "Applicera ändrat läge på arbetskatalogen%s [y,n,q,a,d%s,?]? "
 
 #, c-format
-msgid "Apply deletion to worktree [y,n,q,a,d%s,?]? "
-msgstr "Applicera borttagning på arbetskatalogen [y,n,q,a,d%s,?]? "
+msgid "Apply deletion to worktree%s [y,n,q,a,d%s,?]? "
+msgstr "Applicera borttagning på arbetskatalogen%s [y,n,q,a,d%s,?]? "
 
 #, c-format
-msgid "Apply addition to worktree [y,n,q,a,d%s,?]? "
-msgstr "Applicera tillägg på arbetskatalogen [y,n,q,a,d%s,?]? "
+msgid "Apply addition to worktree%s [y,n,q,a,d%s,?]? "
+msgstr "Applicera tillägg på arbetskatalogen%s [y,n,q,a,d%s,?]? "
 
 #, c-format
-msgid "Apply this hunk to worktree [y,n,q,a,d%s,?]? "
-msgstr "Applicera stycket på arbetskatalogen [y,n,q,a,d%s,?]? "
+msgid "Apply this hunk to worktree%s [y,n,q,a,d%s,?]? "
+msgstr "Applicera stycket på arbetskatalogen%s [y,n,q,a,d%s,?]? "
 
 msgid ""
 "y - apply this hunk to worktree\n"
@@ -442,6 +440,10 @@
 "d - applicera inte stycket eller något av de följande i filen\n"
 
 #, c-format
+msgid "%s cannot be negative"
+msgstr "%s kan inte vara negativt"
+
+#, c-format
 msgid "could not parse hunk header '%.*s'"
 msgstr "kunde inte tolka styckehuvudet ”%.*s”"
 
@@ -536,6 +538,7 @@
 msgid "Nothing was applied.\n"
 msgstr "Ingenting applicerades.\n"
 
+#, c-format
 msgid ""
 "j - go to the next undecided hunk, roll over at the bottom\n"
 "J - go to the next hunk, roll over at the bottom\n"
@@ -547,7 +550,10 @@
 "e - manually edit the current hunk\n"
 "p - print the current hunk\n"
 "P - print the current hunk using the pager\n"
+"> - go to the next file, roll over at the bottom\n"
+"< - go to the previous file, roll over at the top\n"
 "? - print help\n"
+"HUNKS SUMMARY - Hunks: %d, USE: %d, SKIP: %d\n"
 msgstr ""
 "j - gå till nästa obestämda stycke, börja om vid slutet\n"
 "J - gå till nästa stycke, börja om vid slutet\n"
@@ -559,12 +565,30 @@
 "e - redigera aktuellt stycke manuellt\n"
 "p - visa aktuellt stycke\n"
 "P - visa aktuellt stycke i bläddrare\n"
+"> - gå till nästa fil, börja om vid slutet\n"
+"< - gå till föregående fil, börja om vid början\n"
 "? - visa hjälp\n"
+"STYCKESAMMANFATTNING - Stycken: %d, ANVÄND: %d, HOPPA ÖVER: %d\n"
+
+msgid "'git apply' failed"
+msgstr "”git apply” misslyckades"
+
+msgid " (was: y)"
+msgstr " (var: y)"
+
+msgid " (was: n)"
+msgstr " (var: n)"
 
 #, c-format
 msgid "Only one letter is expected, got '%s'"
 msgstr "Förväntade endast en bokstav, fick ”%s”"
 
+msgid "No next file"
+msgstr "Ingen nästa fil"
+
+msgid "No previous file"
+msgstr "Ingen föregående fil"
+
 msgid "No other hunk"
 msgstr "Inget annat stycke"
 
@@ -617,9 +641,6 @@
 msgid "Unknown command '%s' (use '?' for help)"
 msgstr "Okänt kommando ”%s” (använd ”?” för hjälp)"
 
-msgid "'git apply' failed"
-msgstr "”git apply” misslyckades"
-
 msgid "No changes."
 msgstr "Inga ändringar."
 
@@ -627,6 +648,25 @@
 msgstr "Endast binära filer ändrade."
 
 #, c-format
+msgid "Stage mode change [y,n,q,a,d%s,?]? "
+msgstr "Köa ändrat läge [y,n,q,a,d%s,?]? "
+
+#, c-format
+msgid "Stage deletion [y,n,q,a,d%s,?]? "
+msgstr "Köa borttagning [y,n,q,a,d%s,?]? "
+
+#, c-format
+msgid "Stage addition [y,n,q,a,d%s,?]? "
+msgstr "Köa tillägg [y,n,q,a,d%s,?]? "
+
+#, c-format
+msgid "Stage this hunk [y,n,q,a,d%s,?]? "
+msgstr "Köa stycket [y,n,q,a,d%s,?]? "
+
+msgid "Revision does not refer to a commit"
+msgstr "Revisionen pekar inte på en incheckning"
+
+#, c-format
 msgid ""
 "\n"
 "Disable this message with \"git config set advice.%s false\""
@@ -820,14 +860,26 @@
 msgstr "regexec returnerade %d för indata: %s"
 
 #, c-format
-msgid "unable to find filename in patch at line %d"
-msgstr "kan inte hitta filnamn i patchen på rad %d"
+msgid "unable to find filename in patch at %s:%d"
+msgstr "kan inte hitta filnamn i patchen på %s:%d"
+
+#, c-format
+msgid "git apply: bad git-diff - expected /dev/null, got %s at %s:%d"
+msgstr "git apply: dålig git-diff - förväntade /dev/null, fick %s på %s:%d"
 
 #, c-format
 msgid "git apply: bad git-diff - expected /dev/null, got %s on line %d"
 msgstr "git apply: dålig git-diff - förväntade /dev/null, fick %s på rad %d"
 
 #, c-format
+msgid "git apply: bad git-diff - inconsistent new filename at %s:%d"
+msgstr "git apply: dålig git-diff - motsägande nytt filnamn på %s:%d"
+
+#, c-format
+msgid "git apply: bad git-diff - inconsistent old filename at %s:%d"
+msgstr "git apply: dålig git-diff - motsägande gammalt filnamn på %s:%d"
+
+#, c-format
 msgid "git apply: bad git-diff - inconsistent new filename on line %d"
 msgstr "git apply: dålig git-diff - motsägande nytt filnamn på rad %d"
 
@@ -836,10 +888,18 @@
 msgstr "git apply: dålig git-diff - motsägande gammalt filnamn på rad %d"
 
 #, c-format
+msgid "git apply: bad git-diff - expected /dev/null at %s:%d"
+msgstr "git apply: dålig git-diff - förväntade /dev/null på %s:%d"
+
+#, c-format
 msgid "git apply: bad git-diff - expected /dev/null on line %d"
 msgstr "git apply: dålig git-diff - förväntade /dev/null på rad %d"
 
 #, c-format
+msgid "invalid mode at %s:%d: %s"
+msgstr "ogiltigt läge på %s:%d: %s"
+
+#, c-format
 msgid "invalid mode on line %d: %s"
 msgstr "ogiltigt läge på rad %d: %s"
 
@@ -850,17 +910,34 @@
 #, c-format
 msgid ""
 "git diff header lacks filename information when removing %d leading pathname "
+"component at %s:%d"
+msgid_plural ""
+"git diff header lacks filename information when removing %d leading pathname "
+"components at %s:%d"
+msgstr[0] ""
+"git-diff-huvudet saknar filnamnsinformation när %d ledande sökvägskomponent "
+"tas bort på %s:%d"
+msgstr[1] ""
+"git-diff-huvudet saknar filnamnsinformation när %d ledande "
+"sökvägskomponenter tas bort på %s:%d"
+
+#, c-format
+msgid ""
+"git diff header lacks filename information when removing %d leading pathname "
 "component (line %d)"
 msgid_plural ""
 "git diff header lacks filename information when removing %d leading pathname "
 "components (line %d)"
 msgstr[0] ""
-"git-diff-huvudet saknar filnamnsinformation när %d ledande sökvägskomponent\n"
+"git-diff-huvudet saknar filnamnsinformation när %d ledande sökvägskomponent "
 "tas bort (rad %d)"
 msgstr[1] ""
 "git-diff-huvudet saknar filnamnsinformation när %d ledande "
-"sökvägskomponenter\n"
-"tas bort (rad %d)"
+"sökvägskomponenter tas bort (rad %d)"
+
+#, c-format
+msgid "git diff header lacks filename information at %s:%d"
+msgstr "git-diff-huvudet saknar filnamnsinformation på %s:%d"
 
 #, c-format
 msgid "git diff header lacks filename information (line %d)"
@@ -871,8 +948,8 @@
 msgstr "recount: oväntad rad: %.*s"
 
 #, c-format
-msgid "patch fragment without header at line %d: %.*s"
-msgstr "patch-fragment utan huvud på rad %d: %.*s"
+msgid "patch fragment without header at %s:%d: %.*s"
+msgstr "patch-fragment utan huvud på %s:%d: %.*s"
 
 msgid "new file depends on old contents"
 msgstr "ny fil beror på gammalt innehåll"
@@ -881,8 +958,8 @@
 msgstr "borttagen fil har fortfarande innehåll"
 
 #, c-format
-msgid "corrupt patch at line %d"
-msgstr "trasig patch på rad %d"
+msgid "corrupt patch at %s:%d"
+msgstr "trasig patch på %s:%d"
 
 #, c-format
 msgid "new file %s depends on old contents"
@@ -897,16 +974,16 @@
 msgstr "** varning: filen %s blir tom men har inte tagits bort"
 
 #, c-format
-msgid "corrupt binary patch at line %d: %.*s"
-msgstr "trasig binärpatch på rad %d: %.*s"
+msgid "corrupt binary patch at %s:%d: %.*s"
+msgstr "trasig binärpatch på %s:%d: %.*s"
 
 #, c-format
-msgid "unrecognized binary patch at line %d"
-msgstr "binärpatchen på rad %d känns inte igen"
+msgid "unrecognized binary patch at %s:%d"
+msgstr "binärpatchen på %s:%d känns inte igen"
 
 #, c-format
-msgid "patch with only garbage at line %d"
-msgstr "patch med bara skräp på rad %d"
+msgid "patch with only garbage at %s:%d"
+msgstr "patch med bara skräp på %s:%d"
 
 #, c-format
 msgid "unable to read symlink %s"
@@ -1163,6 +1240,14 @@
 msgstr "kan inte läsa indexfilen"
 
 #, c-format
+msgid "option -p expects a non-negative integer, got '%s'"
+msgstr "flaggan -p förväntar ett icke-negativt heltal, fick ”%s”"
+
+#, c-format
+msgid "unable to normalize directory: '%s'"
+msgstr "kan inte normalisera katalogen: ”%s”"
+
+#, c-format
 msgid "can't open patch '%s': %s"
 msgstr "kan inte öppna patchen ”%s”: %s"
 
@@ -1277,7 +1362,7 @@
 msgstr "tolerera felaktigt detekterade saknade nyradstecken vid filslut"
 
 msgid "do not trust the line counts in the hunk headers"
-msgstr "lite inte på antalet linjer i styckehuvuden"
+msgstr "lite inte på antalet rader i styckehuvuden"
 
 msgid "root"
 msgstr "rot"
@@ -1552,7 +1637,7 @@
 "git bisect cannot work properly in this case.\n"
 "Maybe you mistook %s and %s revs?\n"
 msgstr ""
-"Några %s-revisioner är inte föräldrar till %s-revisionen.\n"
+"Några %s-revisioner är inte förfäder till %s-revisionen.\n"
 "git bisect kan inte fungera korrekt i detta fall.\n"
 "Kanske du skrev fel %s- och %s-revisioner?\n"
 
@@ -1807,7 +1892,7 @@
 msgstr "”%s” används redan av arbetskatalogen ”%s”"
 
 msgid "git add [<options>] [--] <pathspec>..."
-msgstr "git add [<flaggor>] [--] <sökväg>..."
+msgstr "git add [<flaggor>] [--] <sökvägsangivelse>..."
 
 #, c-format
 msgid "cannot chmod %cx '%s'"
@@ -1848,6 +1933,9 @@
 msgid "select hunks interactively"
 msgstr "välj stycken interaktivt"
 
+msgid "auto advance to the next file when selecting hunks interactively"
+msgstr "gå automatiskt vidare till nästa fil när stycken väljs interaktivt"
+
 msgid "edit current diff and apply"
 msgstr "redigera aktuell diff och applicera"
 
@@ -2337,6 +2425,10 @@
 msgid "Restrict the missing objects to the current sparse-checkout"
 msgstr "Begränsa saknade objekt till befintlig ”sparse-checkout”"
 
+#, c-format
+msgid "unrecognized argument: %s"
+msgstr "okänt argument: %s"
+
 msgid ""
 "git bisect start [--term-(bad|new)=<term-new> --term-(good|old)=<term-old>]\n"
 "                 [--no-checkout] [--first-parent] [<bad> [<good>...]] [--] "
@@ -2345,7 +2437,7 @@
 "git bisect start [--term-(bad|new)=<term-ny> --term-(good|old)=<term-"
 "gammal>]\n"
 "                 [--no-checkout] [--first-parent] [<dålig> [<bra>...]] [--] "
-"[<sökvägsspecar>...]"
+"[<sökvägsangivelser>...]"
 
 msgid "git bisect (bad|new|<term-new>) [<rev>]"
 msgstr "git bisect (bad|new|<term-ny>) [<incheckning>]"
@@ -2363,7 +2455,7 @@
 msgstr "git bisect replay <loggfil>"
 
 msgid "git bisect run <cmd> [<arg>...]"
-msgstr "git bisect run <kommando> [<argument>]..."
+msgstr "git bisect run <kommando> [<argument>...]"
 
 #, c-format
 msgid "cannot open file '%s' in mode '%s'"
@@ -2421,7 +2513,7 @@
 
 #, c-format
 msgid "Invalid command: you're currently in a %s/%s bisect"
-msgstr "Ogiltigt kommando: du utför just nu en ”bisect” med %s/%s."
+msgstr "Ogiltigt kommando: du utför just nu en ”bisect” med %s/%s"
 
 #, c-format
 msgid ""
@@ -2439,7 +2531,7 @@
 msgstr ""
 "Du måste starta med ”git bisect start”.\n"
 "Du måste sedan ange åtminstone en %s och en %s version.\n"
-"(Du kan använda ”git bisect %s” och ”git bisect %s” för detta.)"
+"Du kan använda ”git bisect %s” och ”git bisect %s” för detta."
 
 #, c-format
 msgid "bisecting only with a %s commit"
@@ -2523,7 +2615,7 @@
 msgstr "Vill du att jag ska göra det åt dig [Y=ja/N=nej]? "
 
 msgid "Please call `--bisect-state` with at least one argument"
-msgstr "Anropa ”--bisect-state” med minst ett argument."
+msgstr "Anropa ”--bisect-state” med minst ett argument"
 
 #, c-format
 msgid "'git bisect %s' can take only one argument."
@@ -3099,10 +3191,10 @@
 msgstr "git version:\n"
 
 msgid "compiler info: "
-msgstr "kompilatorinfo:"
+msgstr "kompilatorinfo: "
 
 msgid "libc info: "
-msgstr "libc-info:"
+msgstr "libc-info: "
 
 msgid "not run from a git repository - no hooks to show\n"
 msgstr "körs inte från ett git-arkiv - inga krokar att visa\n"
@@ -3189,7 +3281,7 @@
 
 #, c-format
 msgid "Created new report at '%s'.\n"
-msgstr "Skapade ny rapport på ”%s”\n"
+msgstr "Skapade ny rapport på ”%s”.\n"
 
 msgid ""
 "git bundle create [-q | --quiet | --progress]\n"
@@ -3223,7 +3315,7 @@
 msgstr "historisk flagga; gör ingenting"
 
 msgid "specify bundle format version"
-msgstr "ange formatversion för bunten."
+msgstr "ange formatversion för bunten"
 
 msgid "Need a repository to create a bundle."
 msgstr "Behöver ett arkiv för att skapa en bunt."
@@ -3232,7 +3324,7 @@
 msgstr "visa inte buntdetaljer"
 
 msgid "need a repository to verify a bundle"
-msgstr "behöver ett arkiv för att bekräfta en bunt."
+msgstr "behöver ett arkiv för att bekräfta en bunt"
 
 #, c-format
 msgid "%s is okay\n"
@@ -3521,18 +3613,6 @@
 msgid "copy out the files from named stage"
 msgstr "kopiera ut filer från namngiven etapp"
 
-msgid "git checkout [<options>] <branch>"
-msgstr "git checkout [<flaggor>] <gren>"
-
-msgid "git checkout [<options>] [<branch>] -- <file>..."
-msgstr "git checkout [<flaggor>] [<gren>] -- <fil>..."
-
-msgid "git switch [<options>] [<branch>]"
-msgstr "git switch [<flaggor>] [<gren>]"
-
-msgid "git restore [<options>] [--source=<branch>] <file>..."
-msgstr "git restore [<flaggor>] [--source=<gren>] <fil>..."
-
 #, c-format
 msgid "path '%s' does not have our version"
 msgstr "sökvägen ”%s” har inte vår version"
@@ -3717,11 +3797,12 @@
 "”%s” kan vara både en lokal fil och en spårande gren.\n"
 "Använd -- (och möjligen --no-guess) för att göra otvetydig"
 
+#, c-format
 msgid ""
 "If you meant to check out a remote tracking branch on, e.g. 'origin',\n"
 "you can do so by fully qualifying the name with the --track option:\n"
 "\n"
-"    git checkout --track origin/<name>\n"
+"    git %s --track origin/<name>\n"
 "\n"
 "If you'd like to always have checkouts of an ambiguous <name> prefer\n"
 "one remote, e.g. the 'origin' remote, consider setting\n"
@@ -3730,7 +3811,7 @@
 "Om du menade checka ut en spårad fjärrgren på t.ex. ”origin”, kan du\n"
 "göra det genom att ange hela namnet med flaggan --track:\n"
 "\n"
-"    git checkout --track origin/<namn>\n"
+"    git %s --track origin/<namn>\n"
 "\n"
 "Om du alltid vill att utcheckningar med tvetydiga <namn> ska\n"
 "föredra en fjärr, t.ex. fjärren ”origin” kan du ställa in\n"
@@ -3879,11 +3960,23 @@
 msgstr "checka ut deras version för ej sammanslagna filer"
 
 msgid "do not limit pathspecs to sparse entries only"
-msgstr "begränsa inte sökvägar till endast glesa poster"
+msgstr "begränsa inte sökvägsangivelser till endast glesa poster"
+
+msgid "git checkout [<options>] <branch>"
+msgstr "git checkout [<flaggor>] <gren>"
+
+msgid "git checkout [<options>] [<branch>] -- <file>..."
+msgstr "git checkout [<flaggor>] [<gren>] -- <fil>..."
+
+msgid "git switch [<options>] [<branch>]"
+msgstr "git switch [<flaggor>] [<gren>]"
+
+msgid "git restore [<options>] [--source=<branch>] <file>..."
+msgstr "git restore [<flaggor>] [--source=<gren>] <fil>..."
 
 #, c-format
 msgid "options '-%c', '-%c', and '%s' cannot be used together"
-msgstr "flaggorna ”%-c”, ”-%c” och ”%s” kan inte användas samtidigt"
+msgstr "flaggorna ”-%c”, ”-%c” och ”%s” kan inte användas samtidigt"
 
 msgid "--track needs a branch name"
 msgstr "--track behöver ett namn på en gren"
@@ -3966,7 +4059,8 @@
 "git clean [-d] [-f] [-i] [-n] [-q] [-e <pattern>] [-x | -X] [--] "
 "[<pathspec>...]"
 msgstr ""
-"git clean [-d] [-f] [-i] [-n] [-q] [-e <mönster>] [-x | -X] [--] <sökväg>..."
+"git clean [-d] [-f] [-i] [-n] [-q] [-e <mönster>] [-x | -X] [--] "
+"[<sökvägsangivelse>...]"
 
 #, c-format
 msgid "Removing %s\n"
@@ -4036,7 +4130,7 @@
 
 #, c-format
 msgid "Input ignore patterns>> "
-msgstr "Ange ignoreringsmönster>>"
+msgstr "Ange ignoreringsmönster>> "
 
 #, c-format
 msgid "WARNING: Cannot find items matched by: %s"
@@ -4602,10 +4696,10 @@
 "           [--date=<datum>] [--cleanup=<läge>] [--[no-]status]\n"
 "           [-i | -o] [--pathspec-from-file=<fil> [--pathspec-file-nul]]\n"
 "           [(--trailer <symbol>[(=|:)<värde>])...] [-S[<nyckel-id>]]\n"
-"           [--] [<sökväg>...]"
+"           [--] [<sökvägsangivelse>...]"
 
 msgid "git status [<options>] [--] [<pathspec>...]"
-msgstr "git status [<flaggor>] [--] <sökväg>..."
+msgstr "git status [<flaggor>] [--] [<sökvägsangivelse>...]"
 
 msgid ""
 "You asked to amend the most recent commit, but doing so would make\n"
@@ -5652,10 +5746,6 @@
 msgid "git diff-pairs -z [<diff-options>]"
 msgstr "git diff-pairs -z [<diff-flaggor>]"
 
-#, c-format
-msgid "unrecognized argument: %s"
-msgstr "okänt argument: %s"
-
 msgid "working without -z is not supported"
 msgstr "stöder inte att arbeta utan -z"
 
@@ -5877,13 +5967,6 @@
 "påträffade den signerade incheckningen %s; använd --signed-commits=<läge> "
 "för att hantera den"
 
-msgid ""
-"'strip-if-invalid' is not a valid mode for git fast-export with --signed-"
-"commits=<mode>"
-msgstr ""
-"”strip-if-invalid” är inte ett giltigt läge för git fast-export med --signed-"
-"commits=<läge>"
-
 #, c-format
 msgid ""
 "omitting tag %s,\n"
@@ -5910,13 +5993,6 @@
 "påträffade den signerade taggen %s; använd --signed-tags=<läge> för att "
 "hantera den"
 
-msgid ""
-"'strip-if-invalid' is not a valid mode for git fast-export with --signed-"
-"tags=<mode>"
-msgstr ""
-"”strip-if-invalid” är inte ett giltigt läge för git fast-export med --signed-"
-"tags=<läge>"
-
 #, c-format
 msgid ""
 "tag %s tags unexported object; use --tag-of-filtered-object=<mode> to handle "
@@ -5942,11 +6018,11 @@
 
 #, c-format
 msgid "unable to open marks file %s for writing."
-msgstr "kan inte öppna märkesfilen %s för skrivning"
+msgstr "kan inte öppna märkesfilen %s för skrivning."
 
 #, c-format
 msgid "unable to write marks file %s."
-msgstr "kan inte skriva märkesfilen %s"
+msgstr "kan inte skriva märkesfilen %s."
 
 #, c-format
 msgid "corrupt mark line: %s"
@@ -6075,7 +6151,7 @@
 
 #, c-format
 msgid "EOF in data (%<PRIuMAX> bytes remaining)"
-msgstr "Filslut i data (%<PRIuMAX> byte återstår):"
+msgstr "Filslut i data (%<PRIuMAX> byte återstår)"
 
 #, c-format
 msgid "unexpected deflate failure: %d"
@@ -6199,7 +6275,7 @@
 
 #, c-format
 msgid "NUL in %s: %s"
-msgstr "NUL  i %s: %s"
+msgstr "NUL i %s: %s"
 
 #, c-format
 msgid "garbage after %s: %s"
@@ -6332,6 +6408,39 @@
 "tar bort ogiltig signatur från incheckning\n"
 "  påstås vara av %s"
 
+#, c-format
+msgid ""
+"replacing invalid signature for commit '%.100s...'\n"
+"  allegedly by %s"
+msgstr ""
+"ersätter ogiltig signatur från incheckning ”%.100s...”\n"
+"  påstås vara av %s"
+
+#, c-format
+msgid ""
+"replacing invalid signature for commit '%.*s'\n"
+"  allegedly by %s"
+msgstr ""
+"ersätter ogiltig signatur från incheckning ”%.*s”\n"
+"  påstås vara av %s"
+
+#, c-format
+msgid ""
+"replacing invalid signature for commit\n"
+"  allegedly by %s"
+msgstr ""
+"ersätter ogiltig signatur från incheckning\n"
+"  påstås vara av %s"
+
+msgid "aborting due to invalid signature"
+msgstr "avbryter på grund av ogiltig signatur"
+
+msgid "signing commits in interoperability mode is unsupported"
+msgstr "signering av incheckningar stöds inte i interoperabilitetsläge"
+
+msgid "failed to sign commit object"
+msgstr "misslyckades signera incheckningsobjekt"
+
 msgid "expected committer but didn't get one"
 msgstr "förväntade incheckare men fick ingen"
 
@@ -6346,6 +6455,9 @@
 msgid "importing a commit signature verbatim"
 msgstr "importerar en incheckningssignatur oförändrad"
 
+msgid "failed to sign tag object"
+msgstr "misslyckades signera taggobjekt"
+
 #, c-format
 msgid "importing a tag signature verbatim for tag '%s'"
 msgstr "importerar en taggsignatur oförändrad för taggen ”%s”"
@@ -6358,13 +6470,6 @@
 msgstr ""
 "upptäckte en signerad tagg; använd --signed-tags=<läge> för att hantera den"
 
-msgid ""
-"'strip-if-invalid' is not a valid mode for git fast-import with --signed-"
-"tags=<mode>"
-msgstr ""
-"”strip-if-invalid” är inte ett giltigt läge för git fast-import med --signed-"
-"tags=<läge>"
-
 #, c-format
 msgid "expected 'from' command, got '%s'"
 msgstr "förväntade ”from”-kommando, fick %s"
@@ -6516,7 +6621,7 @@
 msgstr "Låsfil skapad men inte rapporterad: %s"
 
 msgid "git fetch [<options>] [<repository> [<refspec>...]]"
-msgstr "git fetch [<flaggor>] [<arkiv> [<refspec>...]]"
+msgstr "git fetch [<flaggor>] [<arkiv> [<referensspecifikation>...]]"
 
 msgid "git fetch [<options>] <group>"
 msgstr "git fetch [<flaggor>] <grupp>"
@@ -6844,7 +6949,7 @@
 msgstr "skriv incheckningsgrafen efter hämtning"
 
 msgid "accept refspecs from stdin"
-msgstr "ta emot referenser från standard in"
+msgstr "ta emot referensspecifikationer från standard in"
 
 msgid "--negotiate-only needs one or more --negotiation-tip=*"
 msgstr "--negotiate-only behöver en eller flera --negotiation-tip=*"
@@ -7086,7 +7191,7 @@
 
 #, c-format
 msgid "%s: not a commit"
-msgstr "%s: inte en incheckning!"
+msgstr "%s: inte en incheckning"
 
 #, c-format
 msgid "invalid parameter: expected sha1, got '%s'"
@@ -7922,12 +8027,160 @@
 msgid "'git help config' for more information"
 msgstr "”git help config” för mer information"
 
-msgid ""
-"git hook run [--ignore-missing] [--to-stdin=<path>] <hook-name> [-- <hook-"
-"args>]"
+msgid "git history reword <commit> [--dry-run] [--update-refs=(branches|head)]"
 msgstr ""
-"git hook run [--ignore-missing] [--to-stdin=<sökväg>] <krok-namn> [-- <krok-"
-"argument>]"
+"git history reword <incheckning> [--dry-run] [--update-refs=(branches|head)]"
+
+msgid ""
+"git history split <commit> [--dry-run] [--update-refs=(branches|head)] [--] "
+"[<pathspec>...]"
+msgstr ""
+"git history split <incheckning> [--dry-run] [--update-refs=(branches|head)] "
+"[--] [<sökvägsangivelse>...]"
+
+# %s = action (antagligen ett kommando)
+#, c-format
+msgid ""
+"Please enter the commit message for the %s changes. Lines starting\n"
+"with '%s' will be ignored, and an empty message aborts the commit.\n"
+msgstr ""
+"Ange incheckningsmeddelandet för %s-ändringarna. Rader som inleds\n"
+"med ”%s” kommer ignoreras, och ett tomt meddelande avbryter incheckningen.\n"
+
+#, c-format
+msgid "Aborting commit as launching the editor failed.\n"
+msgstr ""
+"Avbryter incheckning eftersom textredigeringsprogrammet inte kunde startas.\n"
+
+#, c-format
+msgid "unable to parse parent commit %s"
+msgstr "kan inte tolka föräldraincheckningen %s"
+
+#, c-format
+msgid "%s expects one of 'branches' or 'head'"
+msgstr "%s förväntar antingeen ”branches” eller ”head”"
+
+msgid "error preparing revisions"
+msgstr "fel när revisioner skulle förberedas"
+
+msgid "replaying merge commits is not supported yet!"
+msgstr "kan ännu inte spela om sammanslagningsincheckningar!"
+
+msgid "cannot look up HEAD"
+msgstr "kan inte slå upp HEAD"
+
+msgid "cannot determine descendance"
+msgstr "kan inte bestämma härkomst"
+
+msgid ""
+"rewritten commit must be an ancestor of HEAD when using --update-refs=head"
+msgstr ""
+"omformulerad incheckning måste vara förfader till HEAD när --update-"
+"refs=head används"
+
+#, c-format
+msgid "failed to begin ref transaction: %s"
+msgstr "misslyckades påbörja ref-transaktion: %s"
+
+#, c-format
+msgid "failed to update ref '%s': %s"
+msgstr "misslyckades uppdatera referensen ”%s”: %s"
+
+#, c-format
+msgid "failed to commit ref transaction: %s"
+msgstr "misslyckades checka in referenstransaktionen: %s"
+
+msgid "control which refs should be updated"
+msgstr "styr vilka referenser som ska uppdateras"
+
+msgid "perform a dry-run without updating any refs"
+msgstr "utför en testkörning utan att uppdatera några referenser"
+
+msgid "command expects a single revision"
+msgstr "kommandot förväntar en ensam revision"
+
+#, c-format
+msgid "commit cannot be found: %s"
+msgstr "incheckningen hittades inte: %s"
+
+msgid "failed writing reworded commit"
+msgstr "misslyckades skriva omformulerad incheckning"
+
+msgid "failed replaying descendants"
+msgstr "misslyckades spela upp ättlingar på nytt"
+
+msgid "unable to populate index with tree"
+msgstr "kndde inte fylla i indexet med trädet"
+
+msgid "unable to acquire index lock"
+msgstr "kunde inte erhålla indexlås"
+
+msgid "failed reading temporary index"
+msgstr "misslyckades läsa temporärt index"
+
+msgid "failed split tree"
+msgstr "misslyckades dela träd"
+
+msgid "split commit is empty"
+msgstr "incheckning att dela är tom"
+
+msgid "split commit tree matches original commit"
+msgstr ""
+"trädet för delad incheckning är identisk med den ursprungliga incheckningen"
+
+msgid "failed writing first commit"
+msgstr "mislyckades skriva första incheckningen"
+
+msgid "failed writing second commit"
+msgstr "misslyckades skriva andra incheckningen"
+
+msgid "control ref update behavior"
+msgstr "styr beteende för referensuppdatering"
+
+msgid "command expects a committish"
+msgstr "kommandot förväntar en incheckning-igt"
+
+msgid "cannot split up merge commit"
+msgstr "kan inte dela sammanslagnignsincheckning"
+
+msgid ""
+"git hook run [--allow-unknown-hook-name] [--ignore-missing] [--to-"
+"stdin=<path>] <hook-name> [-- <hook-args>]"
+msgstr ""
+"git hook run [--allow-unknown-hook-name] [--ignore-missing] [--to-"
+"stdin=<sökväg>] <krok-namn> [-- <krok-argument>]"
+
+msgid ""
+"git hook list [--allow-unknown-hook-name] [-z] [--show-scope] <hook-name>"
+msgstr ""
+"git hook list [--allow-unknown-hook-name] [-z] [--show-scope] <kroknamn>"
+
+msgid "use NUL as line terminator"
+msgstr "använd NUL som radavdelare"
+
+msgid "show the config scope that defined each hook"
+msgstr "visa konfigurationsomfånget som definierat varje krok"
+
+msgid "allow running a hook with a non-native hook name"
+msgstr "tillåt köra en krok med icke-eget kroknamn"
+
+msgid "you must specify a hook event name to list"
+msgstr "du måste ange ett krokhändelsenamn att visa"
+
+#, c-format
+msgid ""
+"unknown hook event '%s';\n"
+"use --allow-unknown-hook-name to allow non-native hook names"
+msgstr ""
+"okänd krokhändelse ”%s”;\n"
+"använd --allow-unknown-hook-name för att tillåta icke-egna kroknamn"
+
+#, c-format
+msgid "no hooks found for event '%s'"
+msgstr "inga krokar hittades för händelsen ”%s”."
+
+msgid "hook from hookdir"
+msgstr "krok från krokkatalog"
 
 msgid "silently ignore missing requested <hook-name>"
 msgstr "ignorera tyst om önskat <krok-namn> saknas"
@@ -8236,21 +8489,6 @@
 "                       [--parse] [<fil>...]"
 
 #, c-format
-msgid "could not stat %s"
-msgstr "kunde inte ta status på %s"
-
-#, c-format
-msgid "file %s is not a regular file"
-msgstr "filen %s är inte en normal fil"
-
-#, c-format
-msgid "file %s is not writable by user"
-msgstr "filen %s är inte skrivbar av användaren"
-
-msgid "could not open temporary file"
-msgstr "kunde inte öppna temporär fil"
-
-#, c-format
 msgid "could not read input file '%s'"
 msgstr "kunde inte läsa indatafilen ”%s”"
 
@@ -8258,6 +8496,10 @@
 msgstr "kunde inte läsa från standard in"
 
 #, c-format
+msgid "could not write to temporary file '%s'"
+msgstr "kunde inte skriva till temporära filen ”%s”"
+
+#, c-format
 msgid "could not rename temporary file to %s"
 msgstr "kunde inte byta namn på temporär fil till %s"
 
@@ -8282,8 +8524,8 @@
 msgid "output only the trailers"
 msgstr "visa endast släprader"
 
-msgid "do not apply trailer.* configuration variables"
-msgstr "tillämpa inte konfigurationsvariablerna trailer.*"
+msgid "do not apply trailer.<key-alias> configuration variables"
+msgstr "tillämpa inte konfigurationsvariablerna trailer.<nyckel-alias>"
 
 msgid "reformat multiline trailer values as single-line values"
 msgstr "omformatera flerradiga släpradsvärden som enradsvärden"
@@ -8303,22 +8545,23 @@
 msgid "no input file given for in-place editing"
 msgstr "ingen indatafil angiven för redigering på plats"
 
-msgid "last-modified can only operate on one tree at a time"
-msgstr "last-modified kan bara användas på ett träd åt gången"
+msgid "last-modified can only operate on one commit at a time"
+msgstr "last-modified kan bara användas på en incheckning åt gången"
+
+#, c-format
+msgid "revision argument '%s' is a %s, not a commit-ish"
+msgstr "revisionsargumentet ”%s” är en %s, inte en incheckning-igt"
 
 #, c-format
 msgid "unknown last-modified argument: %s"
 msgstr "okänt argument till last-modified: %s"
 
-msgid "unable to setup last-modified"
-msgstr "kan inte ställa in last-modified"
-
 msgid ""
-"git last-modified [--recursive] [--show-trees] [<revision-range>] [[--] "
-"<path>...]"
+"git last-modified [--recursive] [--show-trees] [--max-depth=<depth>] [-z]\n"
+"                  [<revision-range>] [[--] <pathspec>...]"
 msgstr ""
-"git last-modified [--recursive] [--show-trees] [<versionsintervall>] [[--] "
-"<sökväg>...]"
+"git last-modified [--recursive] [--show-trees] [--max-depth=<djup>] [-z]\n"
+"                  [<revisionsintervall>] [[--] <sökväg>...]"
 
 msgid "recurse into subtrees"
 msgstr "rekursera ner i underträd"
@@ -8326,6 +8569,12 @@
 msgid "show tree entries when recursing into subtrees"
 msgstr "visa trädposter under rekursering in i underträd"
 
+msgid "maximum tree depth to recurse"
+msgstr "maximal träddjup att rekursera"
+
+msgid "lines are separated with NUL character"
+msgstr "rader avdelas med NUL-tecken"
+
 msgid "git log [<options>] [<revision-range>] [[--] <path>...]"
 msgstr "git log [<flaggor>] [<versionsintervall>] [[--] <sökväg>...]"
 
@@ -8362,7 +8611,7 @@
 "<funknamn> i <fil>"
 
 msgid "-L<range>:<file> cannot be used with pathspec"
-msgstr "-L<intervall>:<fil> kan inte användas med sökvägsspecifikation"
+msgstr "-L<intervall>:<fil> kan inte användas med sökvägsangivelse"
 
 msgid ""
 "\n"
@@ -8398,6 +8647,18 @@
 msgstr "format.headers utan värde"
 
 #, c-format
+msgid "bad boolean config value '%s' for '%s'"
+msgstr "felaktigt booleskt konfigurationsvärde ”%s” för ”%s”"
+
+#, c-format
+msgid ""
+"'%s' used to accept any value and treat that as 'true'.\n"
+"Now it only accepts boolean values, like what '%s' does.\n"
+msgstr ""
+"”%s” används för att tillåta alla värden och tolka dem som ”true”.\n"
+"Nu stöder det bara booleska värden, så som ”%s” gör.\n"
+
+#, c-format
 msgid "cannot open patch file %s"
 msgstr "kan inte öppna patchfilen %s"
 
@@ -8418,6 +8679,10 @@
 msgstr "misslyckades skapa fil för omslagsbrev"
 
 #, c-format
+msgid "'%s' is not a valid format string"
+msgstr "”%s” är inte en giltig formatsträng"
+
+#, c-format
 msgid "insane in-reply-to: %s"
 msgstr "tokigt in-reply-to: %s"
 
@@ -8478,6 +8743,12 @@
 msgid "generate a cover letter"
 msgstr "generera ett följebrev"
 
+msgid "format-spec"
+msgstr "formatspecifikation"
+
+msgid "format spec used for the commit list in the cover letter"
+msgstr "formatspecifikation används för incheckningslistan i omslagsbrevet"
+
 msgid "use simple number sequence for output file names"
 msgstr "använd enkel nummersekvens för utdatafilnamn"
 
@@ -8602,7 +8873,8 @@
 msgstr "visa ändringar mot <rev> i omslagsbrev eller ensam patch"
 
 msgid "show changes against <refspec> in cover letter or single patch"
-msgstr "visa ändringar mot <refspec> i omslagsbrev eller ensam patch"
+msgstr ""
+"visa ändringar mot <referensspecifikation> i omslagsbrev eller ensam patch"
 
 msgid "percentage by which creation is weighted"
 msgstr "procent som skapelse vägs med"
@@ -9343,11 +9615,20 @@
 msgstr "tillåt skapa mer än ett träd"
 
 msgid ""
-"git multi-pack-index [<options>] write [--preferred-pack=<pack>][--refs-"
-"snapshot=<path>]"
+"git multi-pack-index [<options>] write [--preferred-pack=<pack>]\n"
+"  [--[no-]bitmap] [--[no-]incremental] [--[no-]stdin-packs]\n"
+"  [--refs-snapshot=<path>]"
 msgstr ""
-"git multi-pack-index [<flaggor>] write [--preferred-pack=<paket>] [--refs-"
-"snapshot=<sökväg>]"
+"git multi-pack-index [<flaggor>] write [--preferred-pack=<paket>]\n"
+"  [--[no-]bitmap] [--[no-]incremental] [--[no-]stdin-packs]\n"
+"  [--refs-snapshot=<sökväg>]"
+
+msgid ""
+"git multi-pack-index [<options>] compact [--[no-]incremental]\n"
+"  [--[no-]bitmap] <from> <to>"
+msgstr ""
+"git multi-pack-index [<flaggor>] compact [--[no-]incremental]\n"
+"  [--[no-]bitmap] <från> <till>"
 
 msgid "git multi-pack-index [<options>] verify"
 msgstr "git multi-pack-index [<flaggor>] verify"
@@ -9382,6 +9663,17 @@
 msgid "refs snapshot for selecting bitmap commits"
 msgstr "refs-ögonblicksbild för att välja bitkarte-incheckningar"
 
+#, c-format
+msgid "could not find MIDX: %s"
+msgstr "kunde inte hitta MIDX: %s"
+
+msgid "MIDX compaction endpoints must be unique"
+msgstr "MIDX-kompakteringsändpunkter måste vara unika"
+
+#, c-format
+msgid "MIDX %s must be an ancestor of %s"
+msgstr "MIDX %s måste vara en förfader till %s"
+
 msgid ""
 "during repack, collect pack-files of smaller size into a batch that is "
 "larger than this size"
@@ -9992,14 +10284,14 @@
 msgstr "kunde inte hämta typ för objektet %s i paketet %s"
 
 #, c-format
-msgid "packfile %s is a promisor but --exclude-promisor-objects was given"
-msgstr "packfilen %s är ett kontrakt men --exclude-promisor-objects angavs"
-
-#, c-format
 msgid "could not find pack '%s'"
 msgstr "kunde inte hitta paketet ”%s”"
 
 #, c-format
+msgid "packfile %s is a promisor but --exclude-promisor-objects was given"
+msgstr "packfilen %s är ett kontrakt men --exclude-promisor-objects angavs"
+
+#, c-format
 msgid "packfile %s cannot be accessed"
 msgstr "paketfilen %s kunde inte nås"
 
@@ -10028,9 +10320,6 @@
 "förväntade objekt-id, fick skräp:\n"
 " %s"
 
-msgid "could not load cruft pack .mtimes"
-msgstr "kunde inte läsa .mtimes från onödiga paket"
-
 msgid "cannot open pack index"
 msgstr "kan inte öppna paketfilen"
 
@@ -10271,13 +10560,13 @@
 msgstr "låt tid gå ut för objekt äldre än <tid>"
 
 msgid "limit traversal to objects outside promisor packfiles"
-msgstr "begränsa vandring av objekt utanför kontraktspackfiler."
+msgstr "begränsa vandring av objekt utanför kontraktspackfiler"
 
 msgid "cannot prune in a precious-objects repo"
 msgstr "kan inte rensa i ett ”precious-objekt”-arkiv"
 
 msgid "git pull [<options>] [<repository> [<refspec>...]]"
-msgstr "git pull [<flaggor>] [<arkiv> [<refspec>...]]"
+msgstr "git pull [<flaggor>] [<arkiv> [<referensspecifikation>...]]"
 
 msgid ""
 "There is no candidate for rebasing against among the refs that you just "
@@ -10465,7 +10754,7 @@
 msgstr "kan inte ombasera med lokalt lagrade ändringar i undermodul"
 
 msgid "git push [<options>] [<repository> [<refspec>...]]"
-msgstr "git push [<flaggor>] [<arkiv> [<refspec>...]]"
+msgstr "git push [<flaggor>] [<arkiv> [<referensspecifikation>...]]"
 
 msgid "tag shorthand without <tag>"
 msgstr "taggförkortning utan <tagg>"
@@ -11041,7 +11330,7 @@
 msgstr "avbryt men behåll HEAD där det är"
 
 msgid "edit the todo list during an interactive rebase"
-msgstr "redigera attgöra-listan under interaktiv ombasering."
+msgstr "redigera attgöra-listan under interaktiv ombasering"
 
 msgid "show the patch file being applied or merged"
 msgstr "visa patchen som tillämpas eller slås samman"
@@ -11195,7 +11484,7 @@
 msgstr "felaktig uppström ”%s”"
 
 msgid "Could not create new root commit"
-msgstr "kunde inte skapa ny rotincheckning"
+msgstr "Kunde inte skapa ny rotincheckning"
 
 #, c-format
 msgid "no such branch/commit '%s'"
@@ -11482,7 +11771,7 @@
 msgstr "”git refs verify” tar inget argument"
 
 msgid "git refs list "
-msgstr "git refs list"
+msgstr "git refs list "
 
 msgid "'git refs exists' requires a reference"
 msgstr "”git refs exists” kräver en referens"
@@ -11623,7 +11912,7 @@
 
 #, c-format
 msgid "Could not get fetch map for refspec %s"
-msgstr "Kunde inte hämta mappning för referensspecifikation %s"
+msgstr "Kunde inte hämta mappning för referensspecifikationen %s"
 
 msgid "(matching)"
 msgstr "(träffande)"
@@ -11664,7 +11953,7 @@
 "”förälder” till ”förälder/barn” eller att avnästlar en, t.ex. åt\n"
 "andra hållet.\n"
 "\n"
-"Om så är fallet kan du du lösa det genom att första byta namnet på\n"
+"Om så är fallet kan du lösa det genom att första byta namnet på\n"
 "fjärren mot ett annat namn.\n"
 
 #, c-format
@@ -12260,51 +12549,39 @@
 msgstr "endast ett mönster kan anges med -l"
 
 #, c-format
-msgid "'%s' is not a valid commit-ish for %s"
-msgstr "”%s” är inte en giltig incheckning-igt för %s"
-
-msgid "need some commits to replay"
-msgstr "behöver några incheckningar för omspelning"
-
-msgid "all positive revisions given must be references"
-msgstr "alla positiva revisioner som anges måste vara referenser"
-
-msgid "argument to --advance must be a reference"
-msgstr "argumentet till --advance måste vara en referens"
-
-msgid ""
-"cannot advance target with multiple sources because ordering would be ill-"
-"defined"
-msgstr ""
-"kan inte flytta målet framåt när det finns flera källor eftersom ordningen "
-"inte kan fastställas"
-
-#, c-format
 msgid "invalid %s value: '%s'"
 msgstr "ogiltigt värde för %s: ”%s”"
 
 msgid ""
-"(EXPERIMENTAL!) git replay ([--contained] --onto <newbase> | --advance "
-"<branch>) [--ref-action[=<mode>]] <revision-range>"
+"(EXPERIMENTAL!) git replay ([--contained] --onto=<newbase> | --"
+"advance=<branch> | --revert=<branch>)\n"
+"[--ref=<ref>] [--ref-action=<mode>] <revision-range>"
 msgstr ""
-"(EXPERIMENTELLT!) git replay ([--contained] --onto <nybas> | --advance "
-"<gren>) [--ref-action[=<läge>]] <revisions-intervall>"
-
-msgid "make replay advance given branch"
-msgstr "låt omspelningen flytta den givna grenen framåt"
-
-msgid "replay onto given commit"
-msgstr "spela om ovanpå en given incheckning"
+"(EXPERIMENTELLT!) git replay ([--contained] --onto=<nybas> | --"
+"advance=<gren> | --revert=<gren>)\n"
+"[--ref=<ref>] [--ref-action[=<läge>]] <revisions-intervall>"
 
 msgid "update all branches that point at commits in <revision-range>"
 msgstr ""
 "uppdatera alla grenar som pekar på incheckningar i <revisions-intervall>"
 
+msgid "replay onto given commit"
+msgstr "spela om ovanpå en given incheckning"
+
+msgid "make replay advance given branch"
+msgstr "låt omspelningen flytta den givna grenen framåt"
+
+msgid "revert commits onto given branch"
+msgstr "återställer incheckningar ovanpå given grenen"
+
+msgid "reference to update with result"
+msgstr "referensen add uppdatera med resultatet"
+
 msgid "control ref update behavior (update|print)"
 msgstr "styr beteende för referensuppdatering (update|print)"
 
-msgid "option --onto or --advance is mandatory"
-msgstr "flaggan --onto eller --advance måste anges"
+msgid "exactly one of --onto, --advance, or --revert is required"
+msgstr "exakt en av --onto, --advance eller --revert krävs"
 
 #, c-format
 msgid ""
@@ -12315,30 +12592,12 @@
 "biten i ”struct rev_info” kommer att tvingas"
 
 #, c-format
-msgid "failed to begin ref transaction: %s"
-msgstr "misslyckades påbörja ref-transaktion: %s"
-
-msgid "error preparing revisions"
-msgstr "fel när revisioner skulle förberedas"
-
-msgid "replaying down from root commit is not supported yet!"
-msgstr "kan ännu inte spela om hela vägen ned från rotincheckningen!"
-
-msgid "replaying merge commits is not supported yet!"
-msgstr "kan ännu inte spela om sammanslagningsincheckningar!"
-
-#, c-format
-msgid "failed to update ref '%s': %s"
-msgstr "misslyckades uppdatera referensen ”%s”: %s"
-
-#, c-format
-msgid "failed to commit ref transaction: %s"
-msgstr "misslyckades checka in referenstransaktionen: %s"
-
-#, c-format
 msgid "key '%s' not found"
 msgstr "nyckeln ”%s” hittades inte"
 
+msgid "--keys can only be used with --format=lines or --format=nul"
+msgstr "--keys kan endast användas med --format=lines eller --format=nul"
+
 #, c-format
 msgid "invalid format '%s'"
 msgstr "ogiltigt format ”%s”"
@@ -12352,6 +12611,12 @@
 msgid "print all keys/values"
 msgstr "visa alla nycklar/värden"
 
+msgid "show keys"
+msgstr "visa nycklar"
+
+msgid "--keys cannot be used with a <key> or --all"
+msgstr "--keys kan inte användas med en <nyckel> eller --all"
+
 msgid "unsupported output format"
 msgstr "utdataformatet stöds ej"
 
@@ -12394,6 +12659,18 @@
 msgid "Disk size"
 msgstr "Diskstorlek"
 
+msgid "Largest objects"
+msgstr "Största objekt"
+
+msgid "Maximum size"
+msgstr "Största storlek"
+
+msgid "Maximum parents"
+msgstr "Flest föräldrar"
+
+msgid "Maximum entries"
+msgstr "Flest poster"
+
 msgid "Repository structure"
 msgstr "Arkivstruktur"
 
@@ -12406,7 +12683,8 @@
 msgid ""
 "git rerere [clear | forget <pathspec>... | diff | status | remaining | gc]"
 msgstr ""
-"git rerere [clear | forget <sökväg>... | diff | status | remaining | gc]"
+"git rerere [clear | forget <sökvägsangivelsen>... | diff | status | "
+"remaining | gc]"
 
 msgid "register clean resolutions in index"
 msgstr "registrera rena lösningar i indexet"
@@ -12424,7 +12702,7 @@
 "git reset [--mixed | --soft | --hard | --merge | --keep] [-q] [<incheckning>]"
 
 msgid "git reset [-q] [<tree-ish>] [--] <pathspec>..."
-msgstr "git reset [-q] [<träd-igt>] [--] <sökvägar>..."
+msgstr "git reset [-q] [<träd-igt>] [--] <sökvägsangivelse>..."
 
 msgid ""
 "git reset [-q] [--pathspec-from-file [--pathspec-file-nul]] [<tree-ish>]"
@@ -12432,7 +12710,7 @@
 "git reset [-q] [--pathspec-from-file [--pathspec-file-nul]] [<träd-igt>]"
 
 msgid "git reset --patch [<tree-ish>] [--] [<pathspec>...]"
-msgstr "git reset --patch [<träd-igt>] [--] [<sökvägar>...]"
+msgstr "git reset --patch [<träd-igt>] [--] [<sökvägsangivelse>...]"
 
 msgid "mixed"
 msgstr "blandad"
@@ -12542,7 +12820,7 @@
 
 #, c-format
 msgid "marked counting and '%s' cannot be used together"
-msgstr "markerad räkning och ”%s” kan inte användas samtidigt."
+msgstr "markerad räkning och ”%s” kan inte användas samtidigt"
 
 msgid "git rev-parse --parseopt [<options>] -- [<args>...]"
 msgstr "git rev-parse --parseopt [<options>] -- [<argument>...]"
@@ -12715,7 +12993,7 @@
 msgstr ""
 "git rm [-f | --force] [-n] [-r] [--cached] [--ignore-unmatch]\n"
 "       [--quiet] [--pathspec-from-file=<fil> [--pathspec-file-nul]]\n"
-"       [--] [<sökväg>...]"
+"       [--] [<sökvägsangivelse>...]"
 
 msgid ""
 "the following file has staged content different from both the\n"
@@ -12962,7 +13240,45 @@
 msgstr "hashningsalgoritm"
 
 msgid "Unknown hash algorithm"
-msgstr "okänd hashningsalgoritm"
+msgstr "Okänd hashningsalgoritm"
+
+msgid "assuming SHA-1; use --object-format to override"
+msgstr "antar SHA-1; använd --object-format för att överstyra"
+
+msgid "unable to read header"
+msgstr "kan inte läsa huvud"
+
+msgid "unknown index version"
+msgstr "okänd indexversion"
+
+msgid "unable to read index"
+msgstr "kan inte läsa indexet"
+
+msgid "corrupt index file"
+msgstr "trasig indexfil"
+
+#, c-format
+msgid "unable to read entry %u/%u"
+msgstr "kan inte läsa post %u/%u"
+
+#, c-format
+msgid "unable to read sha1 %u/%u"
+msgstr "kan inte läsa sha1 %u/%u"
+
+#, c-format
+msgid "unable to read crc %u/%u"
+msgstr "kan inte läsa crc %u/%u"
+
+#, c-format
+msgid "unable to read 32b offset %u/%u"
+msgstr "kan inte läsa 32b-offset %u/%u"
+
+msgid "inconsistent 64b offset index"
+msgstr "motsägande 64b-offsetindex"
+
+#, c-format
+msgid "unable to read 64b offset %u"
+msgstr "kan inte läsa 64b-offset %u"
 
 msgid ""
 "git show-ref [--head] [-d | --dereference]\n"
@@ -13212,19 +13528,19 @@
 "git stash store [(-m | --message) <meddelande>] [-q | --quiet] <incheckning>"
 
 msgid ""
-"git stash [push [-p | --patch] [-S | --staged] [-k | --[no-]keep-index] [-q "
+"git stash [push] [-p | --patch] [-S | --staged] [-k | --[no-]keep-index] [-q "
 "| --quiet]\n"
 "          [-u | --include-untracked] [-a | --all] [(-m | --message) "
 "<message>]\n"
 "          [--pathspec-from-file=<file> [--pathspec-file-nul]]\n"
-"          [--] [<pathspec>...]]"
+"          [--] [<pathspec>...]"
 msgstr ""
-"git stash [push [-p | --patch] [S | --staged] [-k | --[no-]keep-index] [-q | "
-"--quiet]\n"
+"git stash [push] [-p | --patch] [S | --staged] [-k | --[no-]keep-index] [-q "
+"| --quiet]\n"
 "          [-u | --include-untracked] [-a | --all] [(-m | --message "
 "<meddelande>]\n"
 "          [--pathspec-from-file=<fil> [--pathspec-file-nul]]\n"
-"          [--] [<sökväg>...]]"
+"          [--] [<sökvägsangivelse>...]"
 
 msgid ""
 "git stash save [-p | --patch] [-S | --staged] [-k | --[no-]keep-index] [-q | "
@@ -13290,7 +13606,7 @@
 msgstr "Slår ihop %s med %s"
 
 msgid "Index was not unstashed."
-msgstr "Indexet har inte tagits upp ur ”stash”:en"
+msgstr "Indexet har inte tagits upp ur ”stash”:en."
 
 msgid "could not restore untracked files from stash"
 msgstr "kunde inte återställa ospårade filer från stash-post"
@@ -13487,6 +13803,9 @@
 msgid "could not get a repository handle for submodule '%s'"
 msgstr "kunde inte få tag i arkivhandtag för undermodulen ”%s”"
 
+msgid "git submodule--helper get-default-remote <path>"
+msgstr "git submodule--helper get-default-remote <sökväg>"
+
 #, c-format
 msgid "No url found for submodule path '%s' in .gitmodules"
 msgstr "Hittade ingen url för undermodulsökvägen ”%s” i .gitmodules"
@@ -13523,6 +13842,16 @@
 msgstr "git submodule foreach [--quiet] [--recursive] [--] <kommando>"
 
 #, c-format
+msgid ""
+"failed to set a valid default config for 'submodule.%s.gitdir'. Please "
+"ensure it is set, for example by running something like: 'git config "
+"submodule.%s.gitdir .git/modules/%s'"
+msgstr ""
+"misslyckades skapa en giltig konfiguration för ”submodule.%s.gitdir”. Se "
+"till att den är satt, till exempel genom att köra något i stil med: ”git "
+"config submodule.%s.gitdir .git/modules/%s”"
+
+#, c-format
 msgid "Failed to register url for submodule path '%s'"
 msgstr "Misslyckades registrera url för undermodulsökväg ”%s”"
 
@@ -13608,6 +13937,27 @@
 msgid "could not fetch a revision for HEAD"
 msgstr "kunde inte hämta en version för HEAD"
 
+msgid "git submodule--helper gitdir <name>"
+msgstr "git submodule--helper gitdir <namn>"
+
+msgid ""
+"could not set core.repositoryformatversion to 1.\n"
+"Please set it for migration to work, for example:\n"
+"git config core.repositoryformatversion 1"
+msgstr ""
+"kunde inte sätta core.repositoryformatversion till 1.\n"
+"Sätt det för att migreringen ska fungera, till exempel:\n"
+"git config core.repositoryformatversion 1"
+
+msgid ""
+"could not enable submodulePathConfig extension. It is required\n"
+"for migration to work. Please enable it in the root repo:\n"
+"git config extensions.submodulePathConfig true"
+msgstr ""
+"kunde inte sätta utökningen submodulePathConfig. Den krävs för\n"
+"att migreringen ska fungera. Aktivera den i rotarkivet:\n"
+"git config extensions.submodulePathConfig true"
+
 #, c-format
 msgid "Synchronizing submodule url for '%s'\n"
 msgstr "Synkroniserar undermodul-url för ”%s”\n"
@@ -13700,10 +14050,6 @@
 msgstr "Värdet ”%s” i submodule.alternateLocation förstås inte"
 
 #, c-format
-msgid "refusing to create/use '%s' in another submodule's git dir"
-msgstr "vägrar skapa/använda ”%s” i en annan undermoduls gitkatalog"
-
-#, c-format
 msgid "directory not empty: '%s'"
 msgstr "katalogen inte tom: ”%s”"
 
@@ -13712,6 +14058,14 @@
 msgstr "misslyckades klona ”%s” till undermodulsökvägen ”%s”"
 
 #, c-format
+msgid ""
+"refusing to create/use '%s' in another submodule's git dir. Enabling "
+"extensions.submodulePathConfig should fix this."
+msgstr ""
+"vägrar skapa/använda ”%s” i en annan undermoduls gitkatalog. Du kan rätta "
+"detta genom att slå på extensions.submodulePathConfig."
+
+#, c-format
 msgid "could not get submodule directory for '%s'"
 msgstr "kunde inte få tag i undermodulskatalog för ”%s”"
 
@@ -13980,7 +14334,7 @@
 
 #, c-format
 msgid "Reactivating local git directory for submodule '%s'\n"
-msgstr "Aktiverar lokal git-katalog för undermodulen ”%s” på nytt.\n"
+msgstr "Aktiverar lokal git-katalog för undermodulen ”%s” på nytt\n"
 
 #, c-format
 msgid "unable to checkout submodule '%s'"
@@ -14132,8 +14486,8 @@
 "\n"
 "Skriv ett meddelande för taggen:\n"
 "  %s\n"
-"Rader som inleds med ”%s” kommer behållas; du kan själv ta bort dem om\n"
-"du vill.\n"
+"Rader som inleds med ”%s” kommer behållas; du kan själv ta bort dem om du "
+"vill.\n"
 
 msgid "unable to sign the tag"
 msgstr "kan inte signera taggen"
@@ -14599,8 +14953,8 @@
 msgid "report pruned working trees"
 msgstr "rapportera borttagna arbetskataloger"
 
-msgid "expire working trees older than <time>"
-msgstr "låt tid gå ut för arbetskataloger äldre än <tid>"
+msgid "prune missing working trees older than <time>"
+msgstr "rensa saknade arbetskataloger äldre än <tid>"
 
 #, c-format
 msgid "'%s' already exists"
@@ -14668,15 +15022,8 @@
 msgid "Preparing worktree (detached HEAD %s)"
 msgstr "Förbereder arbetskatalog (frånkopplat HEAD %s)"
 
-#, c-format
-msgid ""
-"HEAD points to an invalid (or orphaned) reference.\n"
-"HEAD path: '%s'\n"
-"HEAD contents: '%s'"
-msgstr ""
-"HEAD pekar på en ogiltig (eller övergiven) referens.\n"
-"HEAD-sökväg: ”%s”\n"
-"HEAD-innehåll: ”%s”"
+msgid "HEAD points to an invalid (or orphaned) reference.\n"
+msgstr "HEAD pekar på en ogiltig (eller övergiven) referens.\n"
 
 msgid ""
 "No local or remote refs exist despite at least one remote\n"
@@ -14700,7 +15047,7 @@
 msgstr "skapa en ofödd gren"
 
 msgid "populate the new working tree"
-msgstr "befolka den nya arbetskatalogen"
+msgstr "fyll i den nya arbetskatalogen"
 
 msgid "keep the new working tree locked"
 msgstr "låt arbetskatalogen förbli låst"
@@ -14734,8 +15081,9 @@
 msgid "show extended annotations and reasons, if available"
 msgstr "visa utökade annoteringar och grunder, om tillgängliga"
 
-msgid "add 'prunable' annotation to worktrees older than <time>"
-msgstr "lägg till ”prunable”-annoteringar till arbetskataloger äldre än <tid>"
+msgid "add 'prunable' annotation to missing worktrees older than <time>"
+msgstr ""
+"lägg till ”prunable”-annoteringar till saknade arbetskataloger äldre än <tid>"
 
 msgid "terminate records with a NUL character"
 msgstr "avsluta poster med NUL-tecken"
@@ -14915,7 +15263,7 @@
 
 #, c-format
 msgid "unrecognized bundle hash algorithm: %s"
-msgstr "okänd hashningsalgoritm för bunt: ”%s”"
+msgstr "okänd hashningsalgoritm för bunt: %s"
 
 #, c-format
 msgid "unknown capability '%s'"
@@ -14960,7 +15308,7 @@
 
 #, c-format
 msgid "The bundle uses this filter: %s"
-msgstr "Bunten använder detta filter. %s"
+msgstr "Bunten använder detta filter: %s"
 
 msgid "unable to dup bundle descriptor"
 msgstr "kan inte duplicera bunthandtag"
@@ -15130,13 +15478,13 @@
 msgstr "Räkna antal opackade objekt och mängd diskutrymme"
 
 msgid "Retrieve and store user credentials"
-msgstr "Hämta och spara användarreferenser"
+msgstr "Hämta och spara inloggningsuppgifter för användare"
 
 msgid "Helper to temporarily store passwords in memory"
 msgstr "Hjälpprogram för att tillfälligt lagra lösenord i minnet"
 
 msgid "Helper to store credentials on disk"
-msgstr "Hjälpprogram för att lagra användarreferenser på disk"
+msgstr "Hjälpprogram för att lagra inloggningsuppgifter på disk"
 
 msgid "Export a single commit to a CVS checkout"
 msgstr "Exportera en enkel incheckning i en CVS-utcheckning"
@@ -15222,6 +15570,9 @@
 msgid "Display help information about Git"
 msgstr "Visa hjälpinformation om Git"
 
+msgid "EXPERIMENTAL: Rewrite history"
+msgstr "EXPERIMENTELLT: Skriv om historiken"
+
 msgid "Run git hooks"
 msgstr "Kör git-krokar"
 
@@ -15296,7 +15647,7 @@
 msgstr "Kör verktyg för lösning av sammanslagningskonflikter"
 
 msgid "Creates a tag object with extra validation"
-msgstr "skapar ett taggobjekt med extra validering"
+msgstr "Skapar ett taggobjekt med extra validering"
 
 msgid "Build a tree-object from ls-tree formatted text"
 msgstr "Bygg ett trädobjekt från ls-tree-formaterad text"
@@ -15325,8 +15676,8 @@
 msgid "Pack heads and tags for efficient repository access"
 msgstr "Packa huvuden och taggar för effektiv arkivåtkomst"
 
-msgid "Compute unique ID for a patch"
-msgstr "Beräkna unik ID för en patch"
+msgid "Compute unique IDs for patches"
+msgstr "Beräkna unika ID för patchar"
 
 msgid "Prune all unreachable objects from the object database"
 msgstr "Ta bort alla onåbara objekt från objektdatabasen"
@@ -15879,6 +16230,10 @@
 msgstr "kunde inte tolka incheckningen %s"
 
 #, c-format
+msgid "object %s is a %s, not a %s"
+msgstr "objektet %s är en %s, inte en %s"
+
+#, c-format
 msgid "%s %s is not a commit!"
 msgstr "%s %s är inte en incheckning!"
 
@@ -15973,10 +16328,10 @@
 
 #, c-format
 msgid "Invalid path: %s"
-msgstr "ogiltig sökväg: %s"
+msgstr "Ogiltig sökväg: %s"
 
 msgid "Unable to create FSEventStream."
-msgstr "kunde inte skapa FSEventStream."
+msgstr "Kunde inte skapa FSEventStream."
 
 msgid "Failed to start the FSEventStream"
 msgstr "Misslyckades starta FSEventStream:en"
@@ -16279,10 +16634,6 @@
 msgstr "felaktigt numeriskt konfigurationsvärde ”%s” för ”%s” i %s: %s"
 
 #, c-format
-msgid "bad boolean config value '%s' for '%s'"
-msgstr "felaktigt booleskt konfigurationsvärde ”%s” för ”%s”"
-
-#, c-format
 msgid "failed to expand user dir in: '%s'"
 msgstr "misslyckades expandera användarkatalog i: ”%s”"
 
@@ -16498,7 +16849,7 @@
 
 #, c-format
 msgid "Looking up %s ... "
-msgstr "Slår upp %s..."
+msgstr "Slår upp %s... "
 
 #, c-format
 msgid "unable to look up %s (port %s) (%s)"
@@ -16850,7 +17201,7 @@
 "tree"
 msgstr ""
 "Inte ett git-arkiv. Använd --no-index för att jämföra två sökvägar utanför "
-"en arbetskatalog."
+"en arbetskatalog"
 
 msgid ""
 "Limiting comparison with pathspecs is only supported if both paths are "
@@ -16910,7 +17261,7 @@
 
 #, c-format
 msgid "pathspec magic not supported by --follow: %s"
-msgstr "sökvägs-magi stöds inte av --follow: %s"
+msgstr "sökvägsuttryck stöds inte av --follow: %s"
 
 #, c-format
 msgid "options '%s', '%s', '%s', and '%s' cannot be used together"
@@ -16941,7 +17292,7 @@
 "Failed to parse --dirstat/-X option parameter:\n"
 "%s"
 msgstr ""
-"Misslyckades tolka argument till flaggan --dirstat/-X;\n"
+"Misslyckades tolka argument till flaggan --dirstat/-X:\n"
 "%s"
 
 #, c-format
@@ -16952,6 +17303,9 @@
 msgid "unknown value after ws-error-highlight=%.*s"
 msgstr "okänt värde efter ws-error-highlight=%.*s"
 
+msgid "--find-object requires a git repository"
+msgstr "--find-object kräver ett git-arkiv"
+
 #, c-format
 msgid "unable to resolve '%s'"
 msgstr "kan inte slå upp ”%s”"
@@ -17325,9 +17679,6 @@
 msgid "<depth>"
 msgstr "<djup>"
 
-msgid "maximum tree depth to recurse"
-msgstr "maximal träddjup att rekursera"
-
 msgid "<file>"
 msgstr "<fil>"
 
@@ -17672,6 +18023,10 @@
 msgid "git fetch-pack: expected response end packet"
 msgstr "git fetch-pack: förväntade svarsavslutningspaket"
 
+#, c-format
+msgid "couldn't resolve 'auto' filter '%s': %s"
+msgstr "kunde inte slå upp ”auto”-filtret ”%s”: %s"
+
 msgid "no matching remote head"
 msgstr "inget motsvarande fjärrhuvud"
 
@@ -17840,7 +18195,7 @@
 msgstr "misslyckades köra kommandot ”%s”: %s\n"
 
 msgid "could not create temporary file"
-msgstr "kunde inte skapa temporära fil"
+msgstr "kunde inte skapa temporär fil"
 
 #, c-format
 msgid "failed writing detached signature to '%s'"
@@ -18040,7 +18395,7 @@
 
 #, c-format
 msgid "Run '%s' instead [y/N]? "
-msgstr "Köra ”%s” istället (j/N)?"
+msgstr "Köra ”%s” istället (j/N)? "
 
 #, c-format
 msgid "Continuing in %0.1f seconds, assuming that you meant '%s'."
@@ -18088,6 +18443,18 @@
 "Kroken ”%s” ignorerades eftersom den inte är markerad som körbar.\n"
 "Du kan inaktivera varningen med ”git config set advice.ignoredHook false”."
 
+#, c-format
+msgid "disabled hook '%s' has no command configured"
+msgstr "den inaktiverade kroken ”%s” har inget kommando konfigurerat"
+
+#, c-format
+msgid ""
+"'hook.%s.command' must be configured or 'hook.%s.event' must be removed; "
+"aborting."
+msgstr ""
+"”hook.%s.command” måste ställas in, eller så måste ”hook.%s.event” tas bort; "
+"avbryter."
+
 msgid "not a git repository"
 msgstr "inte ett git-arkiv"
 
@@ -18140,6 +18507,23 @@
 
 #, c-format
 msgid ""
+"response requested a delay greater than http.maxRetryTime (%ld > %ld seconds)"
+msgstr ""
+"svaret bad om en fördröjning större än http.maxRetryTime (%ld > %ld sekunder)"
+
+#, c-format
+msgid ""
+"configured http.retryAfter exceeds http.maxRetryTime (%ld > %ld seconds)"
+msgstr ""
+"konfigurerad http.retryAfter är större än http.maxRetryTime (%ld > %ld "
+"sekunder)"
+
+#, c-format
+msgid "rate limited, waiting %ld seconds before retry"
+msgstr "hastighetsbegränsad, nytt försök om %ld sekunder"
+
+#, c-format
+msgid ""
 "number too large to represent as curl_off_t on this platform: %<PRIuMAX>"
 msgstr ""
 "värdet för stort för att representeras som curl_off_t på denna plattform: "
@@ -18221,6 +18605,9 @@
 "ställ in IMAP-mappen med  ”git config imap.folder <mapp>”.\n"
 "(t.ex., ”git config imap.folder Utkast”)"
 
+msgid "'auto' filter not supported by this command"
+msgstr "”auto”-filtret hanteras inte av det här kommandot"
+
 msgid "expected 'tree:<depth>'"
 msgstr "förväntade ”tree:<djup>”"
 
@@ -18239,12 +18626,18 @@
 msgid "must escape char in sub-filter-spec: '%c'"
 msgstr "måste använda specialsekvens i delfilter-spec: ”%c”"
 
+msgid "an 'auto' filter cannot be combined"
+msgstr "ett ”auto”-filter kan inte kombineras"
+
 msgid "expected something after combine:"
 msgstr "förväntade någonting efter combine:"
 
 msgid "multiple filter-specs cannot be combined"
 msgstr "flera filterspecifikationer kan inte kombineras"
 
+msgid "an 'auto' filter is incompatible with any other filter"
+msgstr "ett ”auto”-filter är inkompatibelt med alla andra filter"
+
 msgid "unable to upgrade repository format to support partial clone"
 msgstr "kan inte uppgradera arkivformat till att stöda delvis klon"
 
@@ -18275,23 +18668,43 @@
 msgstr "kan inte läsa in rot-trädet för incheckningen %s"
 
 #, c-format
+msgid "could not write lock pid file '%s'"
+msgstr "kunde inte skriva lås-pidfilen ”%s”"
+
+#, c-format
+msgid "malformed lock pid file '%s'"
+msgstr "felformad lås-pidfil ”%s”"
+
+#, c-format
 msgid ""
-"Unable to create '%s.lock': %s.\n"
+"Unable to create '%s': %s.\n"
 "\n"
-"Another git process seems to be running in this repository, e.g.\n"
-"an editor opened by 'git commit'. Please make sure all processes\n"
-"are terminated then try again. If it still fails, a git process\n"
-"may have crashed in this repository earlier:\n"
-"remove the file manually to continue."
 msgstr ""
-"Kunde inte skapa ”%s.lock”: %s.\n"
+"Kunde inte skapa ”%s”: %s.\n"
 "\n"
-"Det verkar som en annan git-process kör i det här arkivet, t.ex.\n"
-"ett textredigeringsprogram startat av ”git commit”. Se till att\n"
-"alla processer avslutats och försök sedan igen. Om det fortfarande\n"
-"misslyckas kanske en git-process har kraschat i det här arkivet\n"
-"tidigare:\n"
-"ta bort filen manuellt för att fortsätta."
+
+#, c-format
+msgid ""
+"Lock may be held by process %<PRIuMAX>; if no git process is running, the "
+"lock file may be stale (PIDs can be reused)"
+msgstr ""
+"Låset kanske hålls av processen %<PRIuMAX>; om ingen git-process körs kan "
+"låsfilen vara föråldrad (PID:ar kan återanvändas)"
+
+#, c-format
+msgid ""
+"Lock was held by process %<PRIuMAX>, which is no longer running; the lock "
+"file appears to be stale"
+msgstr ""
+"Låset hölls av processen %<PRIuMAX>, som inte längre kör; låsfilen verkar "
+"vara föråldrad"
+
+msgid ""
+"Another git process seems to be running in this repository, or the lock file "
+"may be stale"
+msgstr ""
+"En annan git-process verkar köra i det här arkivet, eller så är låsfilen "
+"föråldrad"
 
 #, c-format
 msgid "Unable to create '%s.lock': %s"
@@ -18598,8 +19011,15 @@
 msgid "malformed line: %s"
 msgstr "felaktig rad: %s"
 
-msgid "could not load pack"
-msgstr "kunde inte läsa paket{"
+#, c-format
+msgid "could not load pack %d"
+msgstr "kunde inte läsa paket %d"
+
+msgid "too many packs, unable to compact"
+msgstr "för många paket, kan inte komprimera"
+
+msgid "could not determine preferred pack"
+msgstr "kunde inte bestämma föredraget paket"
 
 #, c-format
 msgid "unable to link '%s' to '%s'"
@@ -18609,6 +19029,13 @@
 msgid "failed to clear multi-pack-index at %s"
 msgstr "misslyckades städa multi-pack-index på %s"
 
+#, c-format
+msgid "unknown MIDX version: %d"
+msgstr "okänd MIDX-version: %d"
+
+msgid "cannot perform MIDX compaction with v1 format"
+msgstr "kan inte utföra MIDX-komprimering med v1-format"
+
 msgid "ignoring existing multi-pack-index; checksum mismatch"
 msgstr "ignorerar befintlig multi-pack-index; felaktig kontrollsumma"
 
@@ -19036,7 +19463,7 @@
 #.
 #, c-format
 msgid "%s [bad object]"
-msgstr "%s [felaktig objekt]."
+msgstr "%s [felaktig objekt]"
 
 #. TRANSLATORS: This is a line of ambiguous commit
 #. object output. E.g.:
@@ -19187,10 +19614,6 @@
 msgstr "ogiltig objekttyp ”%s”"
 
 #, c-format
-msgid "object %s is a %s, not a %s"
-msgstr "objektet %s är en %s, inte en %s"
-
-#, c-format
 msgid "object %s has unknown type id %d"
 msgstr "objektet %s har okänd typ-id %d"
 
@@ -19199,6 +19622,10 @@
 msgstr "kan inte tolka objektet: %s"
 
 #, c-format
+msgid "unable to open object stream for %s"
+msgstr "kan inte öppna objektström för %s"
+
+#, c-format
 msgid "hash mismatch %s"
 msgstr "hashvärde stämmer inte överens %s"
 
@@ -19214,15 +19641,6 @@
 msgid "%s: ignoring alternate object stores, nesting too deep"
 msgstr "%s: ignorerar supplerande objektlager, för djup nästling"
 
-msgid "unable to fdopen alternates lockfile"
-msgstr "kan inte utföra ”fdopen” på suppleantlåsfil"
-
-msgid "unable to read alternates file"
-msgstr "kan inte läsa ”alternates”-filen"
-
-msgid "unable to move new alternates file into place"
-msgstr "kan inte flytta ny ”alternates”-fil på plats"
-
 #, c-format
 msgid "path '%s' does not exist"
 msgstr "sökvägen ”%s” finns inte"
@@ -19267,6 +19685,15 @@
 msgid "%s is not a valid '%s' object"
 msgstr "%s är inte ett giltigt ”%s”-objekt"
 
+msgid "unable to fdopen alternates lockfile"
+msgstr "kan inte utföra ”fdopen” på suppleantlåsfil"
+
+msgid "unable to read alternates file"
+msgstr "kan inte läsa ”alternates”-filen"
+
+msgid "unable to move new alternates file into place"
+msgstr "kan inte flytta ny ”alternates”-fil på plats"
+
 #, c-format
 msgid "duplicate entry when writing bitmap index: %s"
 msgstr "duplicerad post när bitkarteindex skulle skrivas: %s"
@@ -19475,9 +19902,6 @@
 msgid "multi-pack-index reverse-index chunk is the wrong size"
 msgstr "baklängesindex-stycke för multi-pack-index har fel storlek"
 
-msgid "could not determine preferred pack"
-msgstr "kunde inte bestämma föredraget paket"
-
 msgid "cannot both write and verify reverse index"
 msgstr "kan inte både skriva och bekräfta reverse-index"
 
@@ -19501,6 +19925,10 @@
 msgstr "paketfilen %s kunde inte kopplas%s"
 
 #, c-format
+msgid "could not load .mtimes for cruft pack '%s'"
+msgstr "kunde inte läsa .mtimes från onödiga paketet ”%s”"
+
+#, c-format
 msgid "offset before start of pack index for %s (corrupt index?)"
 msgstr "offset före slutet av packindex för %s (trasigt index?)"
 
@@ -19699,7 +20127,8 @@
 
 msgid ""
 "with --pathspec-from-file, pathspec elements are separated with NUL character"
-msgstr "med --pathspec-from-file, sökvägsangivelser avdelas med NUL-tecken"
+msgstr ""
+"med --pathspec-from-file, sökvägsangivelseposter avdelas med NUL-tecken"
 
 #, c-format
 msgid "bad boolean environment value '%s' for '%s'"
@@ -19738,7 +20167,9 @@
 msgstr "ogiltigt attributnamn %s"
 
 msgid "global 'glob' and 'noglob' pathspec settings are incompatible"
-msgstr "de globala sökvägsinställningarna ”glob” och ”noglob” är inkompatibla"
+msgstr ""
+"de globala sökvägsangivelse-inställningarna ”glob” och ”noglob” är "
+"inkompatibla"
 
 msgid ""
 "global 'literal' pathspec setting is incompatible with all other global "
@@ -19760,7 +20191,7 @@
 
 #, c-format
 msgid "Unimplemented pathspec magic '%c' in '%s'"
-msgstr "Ej implementerat sökvägsuttryckmagi ”%c” i ”%s”"
+msgstr "Ej implementerat sökvägsuttryck ”%c” i ”%s”"
 
 #, c-format
 msgid "%s: 'literal' and 'glob' are incompatible"
@@ -19788,7 +20219,7 @@
 
 #, c-format
 msgid "line is badly quoted: %s"
-msgstr "linjen er felaktigt citerad: %s"
+msgstr "raden er felaktigt citerad: %s"
 
 msgid "unable to write flush packet"
 msgstr "kan inte skriva flush-paket"
@@ -19843,6 +20274,10 @@
 msgid "unable to parse --pretty format"
 msgstr "kan inte tolka format för --pretty"
 
+#, c-format
+msgid "%s is not supported by this command"
+msgstr "%s hanteras inte av det här kommandot"
+
 msgid "lazy fetching disabled; some objects may not be available"
 msgstr ""
 "fördröjd hämtning inaktiverad; några objekt kanske inte är tillgängliga"
@@ -19886,6 +20321,22 @@
 msgstr "servern deklarerade en kontraktsfjärr utan namn eller URL: %s"
 
 #, c-format
+msgid ""
+"Storing new %s from server for remote '%s'.\n"
+"    '%s' -> '%s'\n"
+msgstr ""
+"Sparar ny %s från servern för fjärren ”%s”.\n"
+"    ”%s” -> ”%s”\n"
+
+#, c-format
+msgid "invalid filter '%s' for remote '%s' will not be stored: %s"
+msgstr "ogiltigt filter ”%s” för fjärren ”%s” kommer inte att sparas: %s"
+
+#, c-format
+msgid "invalid token '%s' for remote '%s' will not be stored"
+msgstr "ogiltig symbol ”%s” för fjärren ”%s” kommer inte att sparas"
+
+#, c-format
 msgid "unknown '%s' value for '%s' config option"
 msgstr "okänt värde ”%s” för inställningen ”%s”"
 
@@ -19893,6 +20344,10 @@
 msgid "accepted promisor remote '%s' not found"
 msgstr "godkänd kontraktsfjärr ”%s” hittades inte"
 
+#, c-format
+msgid "promisor remote '%s' advertised invalid filter '%s': %s"
+msgstr "kontraktsfjärren ”%s” annonserade ogiltigt filter ”%s”: %s"
+
 msgid "object-info: expected flush after arguments"
 msgstr "object-info: förväntade ”flush” efter argument"
 
@@ -20153,6 +20608,14 @@
 msgstr "%s: kan inte återgå till kö 0"
 
 #, c-format
+msgid ""
+"Skipping submodule due to ignore=all: %s\n"
+"Use --force if you really want to add the submodule."
+msgstr ""
+"Hoppar över undermodul på grund av ignore=all: %s\n"
+"Använd --force om du verkligen vill lägga till undermodulen."
+
+#, c-format
 msgid "unexpected diff status %c"
 msgstr "diff-status %c förväntades inte"
 
@@ -20527,6 +20990,10 @@
 msgid "no reflog for '%s'"
 msgstr "ingen referenslogg för ”%s”"
 
+#, c-format
+msgid "in '%s' phase, update aborted by the reference-transaction hook"
+msgstr "i ”%s”-fasen, uppdateringen avbröts av referenstransaktionskroken"
+
 msgid "Checking references consistency"
 msgstr "Kontrollerar konsistens för referenser"
 
@@ -20636,9 +21103,6 @@
 msgid "ref updates forbidden inside quarantine environment"
 msgstr "referensuppdateringar förbjudna i karantänmiljö"
 
-msgid "ref updates aborted by hook"
-msgstr "referensuppdateringar avbrutna av krok"
-
 #, c-format
 msgid "'%s' exists; cannot create '%s'"
 msgstr "”%s” finns; kan inte skapa ”%s”"
@@ -20832,6 +21296,14 @@
 msgstr "kan inte nå ”%s” med http.pinnedPubkey inställt till: %s"
 
 #, c-format
+msgid "rate limited by '%s', please try again in %ld seconds"
+msgstr "hastighetsbegränsad av ”%s”, försök igen om %ld sekunder"
+
+#, c-format
+msgid "rate limited by '%s', please try again later"
+msgstr "hastighetsbegränsad av ”%s”, försök igen senare"
+
+#, c-format
 msgid "unable to access '%s': %s"
 msgstr "kan inte komma åt ”%s”: %s"
 
@@ -21011,7 +21483,7 @@
 "Did you mean to create a new branch by pushing to\n"
 "'%s:refs/heads/%s'?"
 msgstr ""
-"<Källa>-delen av ref.spec-en är ett incheckningsobjekt.\n"
+"<Källa>-delen av referensspecifikationen är ett incheckningsobjekt.\n"
 "Var det meningen att skapa en ny gren genom att sända\n"
 "till ”%s:refs/heads/%s”?"
 
@@ -21021,7 +21493,7 @@
 "Did you mean to create a new tag by pushing to\n"
 "'%s:refs/tags/%s'?"
 msgstr ""
-"<Källa>-delen av ref.spec-en är ett taggobjekt.\n"
+"<Källa>-delen av referensspecifikationen är ett taggobjekt.\n"
 "Var det meningen att skapa en ny tagg genom att sända\n"
 "till ”%s:refs/tags/%s”?"
 
@@ -21031,7 +21503,7 @@
 "Did you mean to tag a new tree by pushing to\n"
 "'%s:refs/tags/%s'?"
 msgstr ""
-"<Källa>-delen av ref.spec-en är ett trädobjekt.\n"
+"<Källa>-delen av referensspecifikationen är ett trädobjekt.\n"
 "Var det meningen att tagga ett nytt träd genom att sända\n"
 "till ”%s:refs/tags/%s”?"
 
@@ -21041,14 +21513,16 @@
 "Did you mean to tag a new blob by pushing to\n"
 "'%s:refs/tags/%s'?"
 msgstr ""
-"<Källa>-delen av ref.spec-en är ett blobobjekt.\n"
+"<Källa>-delen av referensspecifikationen är ett blobobjekt.\n"
 "Var det meningen att tagga en ny blob genom att sända\n"
 "till ”%s:refs/tags/%s”?"
 
 #, c-format
 msgid ""
 "The <src> part of the refspec ('%s') is an object ID that doesn't exist.\n"
-msgstr "<Källa>-delen av ref.spec-en (”%s”) är ett objekt-ID som inte finns.\n"
+msgstr ""
+"<Källa>-delen av referensspecifikationen (”%s”) är ett objekt-ID som inte "
+"finns.\n"
 
 #, c-format
 msgid "%s cannot be resolved to branch"
@@ -21064,7 +21538,7 @@
 
 #, c-format
 msgid "dst ref %s receives from more than one src"
-msgstr "fjärr-referensen ”%s” hämtar från mer än en källa"
+msgstr "dst referensen ”%s” hämtar från mer än en källa"
 
 msgid "HEAD does not point to a branch"
 msgstr "HEAD pekar inte på en gren"
@@ -21108,11 +21582,12 @@
 msgstr "* Ignorerar märklig referens ”%s” lokalt"
 
 #, c-format
-msgid "Your branch is based on '%s', but the upstream is gone.\n"
-msgstr "Din gren är baserad på ”%s”, men den har försvunnit uppströms.\n"
-
-msgid "  (use \"git branch --unset-upstream\" to fixup)\n"
-msgstr "  (använd ”git branch --unset-upstream” för att rätta)\n"
+msgid ""
+"ignoring value '%s' for status.compareBranches, only @{upstream} and @{push} "
+"are supported"
+msgstr ""
+"ignorerar värdet ”%s” för status.compareBranches, endast @{upstream} och "
+"@{push} stöds"
 
 #, c-format
 msgid "Your branch is up to date with '%s'.\n"
@@ -21166,6 +21641,13 @@
 msgstr "  (använd ”git pull” om du vill integrera fjärrgrenen med din egen)\n"
 
 #, c-format
+msgid "Your branch is based on '%s', but the upstream is gone.\n"
+msgstr "Din gren är baserad på ”%s”, men den har försvunnit uppströms.\n"
+
+msgid "  (use \"git branch --unset-upstream\" to fixup)\n"
+msgstr "  (använd ”git branch --unset-upstream” för att rätta)\n"
+
+#, c-format
 msgid "cannot parse expected object name '%s'"
 msgstr "kan inte tolka förväntat objektnamn ”%s”"
 
@@ -21240,6 +21722,38 @@
 msgid "replace depth too high for object %s"
 msgstr "ersättningsdjupet för högt för objektet %s"
 
+#, c-format
+msgid "'%s' is not a valid commit-ish for %s"
+msgstr "”%s” är inte en giltig incheckning-igt för %s"
+
+#, c-format
+msgid "argument to %s must be a reference"
+msgstr "argumentet till %s måste vara en referens"
+
+#, c-format
+msgid ""
+"'%s' cannot be used with multiple revision ranges because the ordering would "
+"be ill-defined"
+msgstr ""
+"”%s” kan inte användas med flera revisionsintervall eftersom ordningen inte "
+"kan fastställas"
+
+msgid "need some commits to replay"
+msgstr "behöver några incheckningar för omspelning"
+
+msgid "all positive revisions given must be references"
+msgstr "alla positiva revisioner som anges måste vara referenser"
+
+msgid "'--ref' cannot be used with multiple revision ranges"
+msgstr "”--ref” kan inte användas med flera revisionsintervall"
+
+#, c-format
+msgid "'%s' is not a valid refname"
+msgstr "”%s” är inte ett giltigt referensnamn"
+
+msgid "compatibility hash algorithm support requires Rust"
+msgstr "kompatibilitets-hashningsalgoritmen kräver Rust"
+
 msgid "corrupt MERGE_RR"
 msgstr "trasig MERGE_RR"
 
@@ -21309,7 +21823,7 @@
 
 #, c-format
 msgid "failed to find tree of %s"
-msgstr "misslyckades hitta trädet för %s."
+msgstr "misslyckades hitta trädet för %s"
 
 #, c-format
 msgid "unsupported section for hidden refs: %s"
@@ -21968,10 +22482,6 @@
 msgstr "”%s” är inte en giltig etikett"
 
 #, c-format
-msgid "'%s' is not a valid refname"
-msgstr "”%s” är inte ett giltigt referensnamn"
-
-#, c-format
 msgid "update-ref requires a fully qualified refname e.g. refs/heads/%s"
 msgstr "update-ref kräver ett fullständigt referensnamn, t.ex. refs/heads/%s"
 
@@ -22064,6 +22574,15 @@
 msgid "cannot revert during a cherry-pick."
 msgstr "kan inte utföra ”revert” under en ”cherry-pick”."
 
+msgid "trailers file contains empty line"
+msgstr "släpradsfilen innehåller tomrad"
+
+msgid "trailers file is empty"
+msgstr "släpradsfilen är tom"
+
+msgid "cannot read trailers files"
+msgstr "kan inte läsa släpradsfiler"
+
 msgid "unusable squash-onto"
 msgstr "oanvändbar squash-onto"
 
@@ -22549,6 +23068,14 @@
 "\n"
 "\tgit config --global --add safe.directory %s"
 
+#, c-format
+msgid "error reading '%s'"
+msgstr "fel vid läsning av ”%s”"
+
+#, c-format
+msgid "not a regular file: '%s'"
+msgstr "Inte en vanlig fil: ”%s”"
+
 msgid "Unable to read current working directory"
 msgstr "Kan inte läsa aktuell arbetskatalog"
 
@@ -22574,6 +23101,10 @@
 msgstr "kan inte använda naket arkiv ”%s” (safe.bareRepository är ”%s”)"
 
 #, c-format
+msgid "unknown ref storage format: '%s'"
+msgstr "okänt format för lagring av referenser: ”%s”"
+
+#, c-format
 msgid ""
 "problem with core.sharedRepository filemode value (0%.3o).\n"
 "The owner of files must always have read and write permissions."
@@ -22807,7 +23338,7 @@
 "Could not run 'git rev-list <commits> --not --remotes -n 1' command in "
 "submodule %s"
 msgstr ""
-"kunde inte köra ”git rev-list <incheckningar> --not --remotes -n 1” i "
+"Kunde inte köra ”git rev-list <incheckningar> --not --remotes -n 1” i "
 "undermodulen ”%s”"
 
 #, c-format
@@ -22911,10 +23442,6 @@
 msgstr "kunde inte slå upp namnet för undermodulen ”%s”"
 
 #, c-format
-msgid "refusing to move '%s' into an existing git dir"
-msgstr "vägrar flytta ”%s” till en befintlig gitkatalog"
-
-#, c-format
 msgid ""
 "Migrating git directory of '%s%s' from\n"
 "'%s' to\n"
@@ -22932,6 +23459,29 @@
 msgstr "ls-tree returnerade en oväntad returkod %d"
 
 #, c-format
+msgid ""
+"the 'submodule.%s.gitdir' config does not exist for module '%s'. Please "
+"ensure it is set, for example by running something like: 'git config "
+"submodule.%s.gitdir .git/modules/%s'. For details see the "
+"extensions.submodulePathConfig documentation."
+msgstr ""
+"konfigurationen ”submodule.%s.gitdir” saknas för modulen ”%s”. Se till att "
+"den är satt, till exempel genom att köra något i stil med: ”git config "
+"submodule.%s.gitdir .git/modules/%s”. För detaljer, se dokumentationen för "
+"extensions.submodulePathConfig."
+
+msgid ""
+"enabling extensions.submodulePathConfig might fix the following error, if "
+"it's not already enabled."
+msgstr ""
+"följande fel kanske kan fixas genom att aktivera "
+"extensions.submodulePathConfig, om det inte redan är aktiverat."
+
+#, c-format
+msgid "refusing to create/use '%s' in another submodule's  git dir."
+msgstr "vägrar skapa/använda ”%s” i en annan undermoduls gitkatalog."
+
+#, c-format
 msgid "failed to lstat '%s'"
 msgstr "misslyckades ta status (lstat) på ”%s”"
 
@@ -22984,6 +23534,10 @@
 msgid "too many commits marked reachable"
 msgstr "för många incheckningar markerade nåbara"
 
+#, c-format
+msgid "could not find MIDX with checksum %s"
+msgstr "kunde inte hitta MIDX med kontrollsumma %s"
+
 msgid "could not determine MIDX preferred pack"
 msgstr "kunde inte bestämma det föredragna MIDX-paketet"
 
@@ -23041,7 +23595,7 @@
 msgstr "ballasttecken"
 
 msgid "token"
-msgstr "igenkänningstecken"
+msgstr "symbol"
 
 msgid "command token to send to the server"
 msgstr "igenkänningstecken för kommando att sända till servern"
@@ -23076,6 +23630,28 @@
 msgid "empty trailer token in trailer '%.*s'"
 msgstr "tom släpradssymbol i släpraden ”%.*s”"
 
+msgid "empty --trailer argument"
+msgstr "tomt --trailer-argument"
+
+#, c-format
+msgid "invalid trailer '%s': missing key before separator"
+msgstr "ogiltig släprad ”%s”: saknar nyckel före avdelare"
+
+#, c-format
+msgid "could not stat %s"
+msgstr "kunde inte ta status på %s"
+
+#, c-format
+msgid "file %s is not a regular file"
+msgstr "filen %s är inte en normal fil"
+
+#, c-format
+msgid "file %s is not writable by user"
+msgstr "filen %s är inte skrivbar av användaren"
+
+msgid "could not write to temporary file"
+msgstr "kunde inte skriva till temporär fil"
+
 msgid "full write to remote helper failed"
 msgstr "komplett skrivning till fjärrhjälpare misslyckades"
 
@@ -23096,8 +23672,7 @@
 
 msgid "this remote helper should implement refspec capability"
 msgstr ""
-"fjärrhjälparen behöver implementera förmåga för referensspecifikationer "
-"(refspec)"
+"denna fjärrhjälpare behöver implementera förmåga för referensspecifikationer"
 
 #, c-format
 msgid "%s unexpectedly said: '%s'"
@@ -24407,8 +24982,14 @@
 msgstr ""
 "Följande filer är åttabitars, men anger inte en Content-Transfer-Encoding.\n"
 
-msgid "Which 8bit encoding should I declare [UTF-8]? "
-msgstr "Vilken åttabitarsteckenkodning ska jag ange [UTF-8]? "
+msgid "Declare which 8bit encoding to use [default: UTF-8]? "
+msgstr "Ange åttabitarsteckenkodning att använda [förval: UTF-8]? "
+
+#, perl-format
+msgid "'%s' does not appear to be a valid charset name. Use it anyway [y/N]? "
+msgstr ""
+"”%s” verkar inte vara ett giltigt teckenkodningsnamn. Använda det ändå [y/"
+"N]? "
 
 #, perl-format
 msgid ""
@@ -24447,6 +25028,10 @@
 msgid "CA path \"%s\" does not exist"
 msgstr "CA-sökvägen ”%s” finns inte"
 
+#, perl-format
+msgid "Only client key \"%s\" specified"
+msgstr "Ingen klientnyckel ”%s” har angivits"
+
 msgid ""
 "    The Cc list above has been expanded by additional\n"
 "    addresses found in the patch commit message. By default\n"
diff --git a/po/tr.po b/po/tr.po
index c459b4a..487e0bd 100644
--- a/po/tr.po
+++ b/po/tr.po
@@ -97,8 +97,8 @@
 msgstr ""
 "Project-Id-Version: Git Turkish Localization Project\n"
 "Report-Msgid-Bugs-To: Git Mailing List <git@vger.kernel.org>\n"
-"POT-Creation-Date: 2026-01-27 22:54+0300\n"
-"PO-Revision-Date: 2026-01-27 22:00+0300\n"
+"POT-Creation-Date: 2026-04-16 01:14+0300\n"
+"PO-Revision-Date: 2026-04-04 22:30+0300\n"
 "Last-Translator: Emir SARI <emir_sari@icloud.com>\n"
 "Language-Team: Turkish <kde-i18n-doc@kde.org>\n"
 "Language: tr\n"
@@ -109,10 +109,6 @@
 "X-Generator: Lokalize 25.07.70\n"
 
 #, c-format
-msgid "%s cannot be negative"
-msgstr "%s negatif olamaz"
-
-#, c-format
 msgid "Huh (%s)?"
 msgstr "Pardon (%s)?"
 
@@ -133,7 +129,7 @@
 
 #, c-format
 msgid "could not stage '%s'"
-msgstr "'%s' hazırlanamadı"
+msgstr "\"%s\" hazırlanamadı"
 
 msgid "could not write index"
 msgstr "indeks yazılamadı"
@@ -150,7 +146,7 @@
 
 #, c-format
 msgid "make_cache_entry failed for path '%s'"
-msgstr "'%s' yolu için make_cache_entry başarısız oldu"
+msgstr "\"%s\" yolu için make_cache_entry başarısız oldu"
 
 msgid "Revert"
 msgstr "Geri al"
@@ -266,20 +262,20 @@
 msgstr "Güle güle.\n"
 
 #, c-format
-msgid "Stage mode change [y,n,q,a,d%s,?]? "
-msgstr "Kip değişimi hazırlansın mı [y,n,q,a,d%s,?]? "
+msgid "Stage mode change%s [y,n,q,a,d%s,?]? "
+msgstr "Kip değişimi hazırlansın mı%s [y,n,q,a,d%s,?]? "
 
 #, c-format
-msgid "Stage deletion [y,n,q,a,d%s,?]? "
-msgstr "Silme hazırlansın mı [y,n,q,a,d%s,?]? "
+msgid "Stage deletion%s [y,n,q,a,d%s,?]? "
+msgstr "Silme hazırlansın mı%s [y,n,q,a,d%s,?]? "
 
 #, c-format
-msgid "Stage addition [y,n,q,a,d%s,?]? "
-msgstr "Ekleme hazırlansın mı [y,n,q,a,d%s,?]? "
+msgid "Stage addition%s [y,n,q,a,d%s,?]? "
+msgstr "Ekleme hazırlansın mı%s [y,n,q,a,d%s,?]? "
 
 #, c-format
-msgid "Stage this hunk [y,n,q,a,d%s,?]? "
-msgstr "Bu parça hazırlansın mı [y,n,q,a,d%s,?]? "
+msgid "Stage this hunk%s [y,n,q,a,d%s,?]? "
+msgstr "Bu parça hazırlansın mı%s [y,n,q,a,d%s,?]? "
 
 msgid ""
 "If the patch applies cleanly, the edited hunk will immediately be marked for "
@@ -302,20 +298,20 @@
 "d - bu parçayı veya sonraki parçalardan herhangi birini hazırlama\n"
 
 #, c-format
-msgid "Stash mode change [y,n,q,a,d%s,?]? "
-msgstr "Kip değişimi zulalansın mı [y,n,q,a,d%s,?]? "
+msgid "Stash mode change%s [y,n,q,a,d%s,?]? "
+msgstr "Kip değişimi zulalansın mı%s [y,n,q,a,d%s,?]? "
 
 #, c-format
-msgid "Stash deletion [y,n,q,a,d%s,?]? "
-msgstr "Silme zulalansın mı [y,n,q,a,d%s,?]? "
+msgid "Stash deletion%s [y,n,q,a,d%s,?]? "
+msgstr "Silme zulalansın mı%s [y,n,q,a,d%s,?]? "
 
 #, c-format
-msgid "Stash addition [y,n,q,a,d%s,?]? "
-msgstr "Ekleme zulalansın mı [y,n,q,a,d%s,?]? "
+msgid "Stash addition%s [y,n,q,a,d%s,?]? "
+msgstr "Ekleme zulalansın mı%s [y,n,q,a,d%s,?]? "
 
 #, c-format
-msgid "Stash this hunk [y,n,q,a,d%s,?]? "
-msgstr "Bu parça zulalansın mı [y,n,q,a,d%s,?]? "
+msgid "Stash this hunk%s [y,n,q,a,d%s,?]? "
+msgstr "Bu parça zulalansın mı%s [y,n,q,a,d%s,?]? "
 
 msgid ""
 "If the patch applies cleanly, the edited hunk will immediately be marked for "
@@ -338,20 +334,20 @@
 "d - bu parçayı veya sonraki parçalardan herhangi birini zulalama\n"
 
 #, c-format
-msgid "Unstage mode change [y,n,q,a,d%s,?]? "
-msgstr "Kip değişimi hazırlıktan çıkarılsın mı [y,n,q,a,d%s,?]? "
+msgid "Unstage mode change%s [y,n,q,a,d%s,?]? "
+msgstr "Kip değişimi hazırlıktan çıkarılsın mı%s [y,n,q,a,d%s,?]? "
 
 #, c-format
-msgid "Unstage deletion [y,n,q,a,d%s,?]? "
-msgstr "Silme hazırlıktan çıkarılsın mı [y,n,q,a,d%s,?]? "
+msgid "Unstage deletion%s [y,n,q,a,d%s,?]? "
+msgstr "Silme hazırlıktan çıkarılsın mı%s [y,n,q,a,d%s,?]? "
 
 #, c-format
-msgid "Unstage addition [y,n,q,a,d%s,?]? "
-msgstr "Ekleme hazırlıktan çıkarılsın mı [y,n,q,a,d%s,?]? "
+msgid "Unstage addition%s [y,n,q,a,d%s,?]? "
+msgstr "Ekleme hazırlıktan çıkarılsın mı%s [y,n,q,a,d%s,?]? "
 
 #, c-format
-msgid "Unstage this hunk [y,n,q,a,d%s,?]? "
-msgstr "Bu parça hazırlıktan çıkarılsın mı [y,n,q,a,d%s,?]? "
+msgid "Unstage this hunk%s [y,n,q,a,d%s,?]? "
+msgstr "Bu parça hazırlıktan çıkarılsın mı%s [y,n,q,a,d%s,?]? "
 
 msgid ""
 "If the patch applies cleanly, the edited hunk will immediately be marked for "
@@ -374,20 +370,20 @@
 "d - bu parçayı veya sonraki parçalardan herhangi birini hazırlıktan çıkarma\n"
 
 #, c-format
-msgid "Apply mode change to index [y,n,q,a,d%s,?]? "
-msgstr "Kip değişimi indekse uygulansın mı [y,n,q,a,d%s,?]? "
+msgid "Apply mode change to index%s [y,n,q,a,d%s,?]? "
+msgstr "Kip değişimi indekse uygulansın mı%s [y,n,q,a,d%s,?]? "
 
 #, c-format
-msgid "Apply deletion to index [y,n,q,a,d%s,?]? "
-msgstr "Silme indekse uygulansın mı [y,n,q,a,d%s,?]? "
+msgid "Apply deletion to index%s [y,n,q,a,d%s,?]? "
+msgstr "Silme indekse uygulansın mı%s [y,n,q,a,d%s,?]? "
 
 #, c-format
-msgid "Apply addition to index [y,n,q,a,d%s,?]? "
-msgstr "Ekleme indekse uygulansın mı [y,n,q,a,d%s,?]? "
+msgid "Apply addition to index%s [y,n,q,a,d%s,?]? "
+msgstr "Ekleme indekse uygulansın mı%s [y,n,q,a,d%s,?]? "
 
 #, c-format
-msgid "Apply this hunk to index [y,n,q,a,d%s,?]? "
-msgstr "Bu parça indekse uygulansın mı [y,n,q,a,d%s,?]? "
+msgid "Apply this hunk to index%s [y,n,q,a,d%s,?]? "
+msgstr "Bu parça indekse uygulansın mı%s [y,n,q,a,d%s,?]? "
 
 msgid ""
 "If the patch applies cleanly, the edited hunk will immediately be marked for "
@@ -410,20 +406,20 @@
 "d - bu parçayı veya sonraki parçalardan herhangi birini uygulama\n"
 
 #, c-format
-msgid "Discard mode change from worktree [y,n,q,a,d%s,?]? "
-msgstr "Kip değişimi çalışma ağacından atılsın mı [y,n,q,a,d%s,?]? "
+msgid "Discard mode change from worktree%s [y,n,q,a,d%s,?]? "
+msgstr "Kip değişimi çalışma ağacından atılsın mı%s [y,n,q,a,d%s,?]? "
 
 #, c-format
-msgid "Discard deletion from worktree [y,n,q,a,d%s,?]? "
-msgstr "Silme çalışma ağacından atılsın mı [y,n,q,a,d%s,?]? "
+msgid "Discard deletion from worktree%s [y,n,q,a,d%s,?]? "
+msgstr "Silme çalışma ağacından atılsın mı%s [y,n,q,a,d%s,?]? "
 
 #, c-format
-msgid "Discard addition from worktree [y,n,q,a,d%s,?]? "
-msgstr "Ekleme çalışma ağacından atılsın mı [y,n,q,a,d%s,?]? "
+msgid "Discard addition from worktree%s [y,n,q,a,d%s,?]? "
+msgstr "Ekleme çalışma ağacından atılsın mı%s [y,n,q,a,d%s,?]? "
 
 #, c-format
-msgid "Discard this hunk from worktree [y,n,q,a,d%s,?]? "
-msgstr "Bu parça çalışma ağacından atılsın mı [y,n,q,a,d%s,?]? "
+msgid "Discard this hunk from worktree%s [y,n,q,a,d%s,?]? "
+msgstr "Bu parça çalışma ağacından atılsın mı%s [y,n,q,a,d%s,?]? "
 
 msgid ""
 "If the patch applies cleanly, the edited hunk will immediately be marked for "
@@ -446,21 +442,21 @@
 "d - bu parçayı veya sonraki parçalardan herhangi birini atma\n"
 
 #, c-format
-msgid "Discard mode change from index and worktree [y,n,q,a,d%s,?]? "
+msgid "Discard mode change from index and worktree%s [y,n,q,a,d%s,?]? "
 msgstr ""
-"Kip değişimi indeksten ve çalışma ağacından atılsın mı [y,n,q,a,d%s,?]? "
+"Kip değişimi indeksten ve çalışma ağacından atılsın mı%s [y,n,q,a,d%s,?]? "
 
 #, c-format
-msgid "Discard deletion from index and worktree [y,n,q,a,d%s,?]? "
-msgstr "Silme indeksten ve çalışma ağacından atılsın mı [y,n,q,a,d%s,?]? "
+msgid "Discard deletion from index and worktree%s [y,n,q,a,d%s,?]? "
+msgstr "Silme indeksten ve çalışma ağacından atılsın mı%s [y,n,q,a,d%s,?]? "
 
 #, c-format
-msgid "Discard addition from index and worktree [y,n,q,a,d%s,?]? "
-msgstr "Ekleme indeksten ve çalışma ağacından atılsın mı [y,n,q,a,d%s,?]? "
+msgid "Discard addition from index and worktree%s [y,n,q,a,d%s,?]? "
+msgstr "Ekleme indeksten ve çalışma ağacından atılsın mı%s [y,n,q,a,d%s,?]? "
 
 #, c-format
-msgid "Discard this hunk from index and worktree [y,n,q,a,d%s,?]? "
-msgstr "Bu parça indeksten ve çalışma ağacından atılsın mı [y,n,q,a,d%s,?]? "
+msgid "Discard this hunk from index and worktree%s [y,n,q,a,d%s,?]? "
+msgstr "Bu parça indeksten ve çalışma ağacından atılsın mı%s [y,n,q,a,d%s,?]? "
 
 msgid ""
 "y - discard this hunk from index and worktree\n"
@@ -476,21 +472,21 @@
 "d - bu parçayı veya sonraki parçalardan herhangi birini atma\n"
 
 #, c-format
-msgid "Apply mode change to index and worktree [y,n,q,a,d%s,?]? "
+msgid "Apply mode change to index and worktree%s [y,n,q,a,d%s,?]? "
 msgstr ""
-"Kip değişimi indekse ve çalışma ağacına uygulansın mı [y,n,q,a,d%s,?]? "
+"Kip değişimi indekse ve çalışma ağacına uygulansın mı%s [y,n,q,a,d%s,?]? "
 
 #, c-format
-msgid "Apply deletion to index and worktree [y,n,q,a,d%s,?]? "
-msgstr "Silme indekse ve çalışma ağacına uygulansın mı [y,n,q,a,d%s,?]? "
+msgid "Apply deletion to index and worktree%s [y,n,q,a,d%s,?]? "
+msgstr "Silme indekse ve çalışma ağacına uygulansın mı%s [y,n,q,a,d%s,?]? "
 
 #, c-format
-msgid "Apply addition to index and worktree [y,n,q,a,d%s,?]? "
-msgstr "Ekleme indekse ve çalışma ağacına uygulansın mı [y,n,q,a,d%s,?]? "
+msgid "Apply addition to index and worktree%s [y,n,q,a,d%s,?]? "
+msgstr "Ekleme indekse ve çalışma ağacına uygulansın mı%s [y,n,q,a,d%s,?]? "
 
 #, c-format
-msgid "Apply this hunk to index and worktree [y,n,q,a,d%s,?]? "
-msgstr "Bu parça indekse ve çalışma ağacına uygulansın mı [y,n,q,a,d%s,?]? "
+msgid "Apply this hunk to index and worktree%s [y,n,q,a,d%s,?]? "
+msgstr "Bu parça indekse ve çalışma ağacına uygulansın mı%s [y,n,q,a,d%s,?]? "
 
 msgid ""
 "y - apply this hunk to index and worktree\n"
@@ -506,20 +502,20 @@
 "d - bu parçayı veya sonraki parçalardan herhangi birini uygulama\n"
 
 #, c-format
-msgid "Apply mode change to worktree [y,n,q,a,d%s,?]? "
-msgstr "Kip değişimi çalışma ağacına uygulansın mı [y,n,q,a,d%s,?]? "
+msgid "Apply mode change to worktree%s [y,n,q,a,d%s,?]? "
+msgstr "Kip değişimi çalışma ağacına uygulansın mı%s [y,n,q,a,d%s,?]? "
 
 #, c-format
-msgid "Apply deletion to worktree [y,n,q,a,d%s,?]? "
-msgstr "Silme çalışma ağacına uygulansın mı [y,n,q,a,d%s,?]? "
+msgid "Apply deletion to worktree%s [y,n,q,a,d%s,?]? "
+msgstr "Silme çalışma ağacına uygulansın mı%s [y,n,q,a,d%s,?]? "
 
 #, c-format
-msgid "Apply addition to worktree [y,n,q,a,d%s,?]? "
-msgstr "Ekleme çalışma ağacına uygulansın mı [y,n,q,a,d%s,?]? "
+msgid "Apply addition to worktree%s [y,n,q,a,d%s,?]? "
+msgstr "Ekleme çalışma ağacına uygulansın mı%s [y,n,q,a,d%s,?]? "
 
 #, c-format
-msgid "Apply this hunk to worktree [y,n,q,a,d%s,?]? "
-msgstr "Bu parça çalışma ağacına uygulansın mı [y,n,q,a,d%s,?]? "
+msgid "Apply this hunk to worktree%s [y,n,q,a,d%s,?]? "
+msgstr "Bu parça çalışma ağacına uygulansın mı%s [y,n,q,a,d%s,?]? "
 
 msgid ""
 "y - apply this hunk to worktree\n"
@@ -535,6 +531,10 @@
 "d - bu parçayı veya sonraki parçalardan herhangi birini uygulama\n"
 
 #, c-format
+msgid "%s cannot be negative"
+msgstr "%s negatif olamaz"
+
+#, c-format
 msgid "could not parse hunk header '%.*s'"
 msgstr "parça üstbilgisi '%.*s' ayrıştırılamadı"
 
@@ -629,6 +629,7 @@
 msgid "Nothing was applied.\n"
 msgstr "Hiçbir şey uygulanmadı.\n"
 
+#, c-format
 msgid ""
 "j - go to the next undecided hunk, roll over at the bottom\n"
 "J - go to the next hunk, roll over at the bottom\n"
@@ -640,7 +641,10 @@
 "e - manually edit the current hunk\n"
 "p - print the current hunk\n"
 "P - print the current hunk using the pager\n"
+"> - go to the next file, roll over at the bottom\n"
+"< - go to the previous file, roll over at the top\n"
 "? - print help\n"
+"HUNKS SUMMARY - Hunks: %d, USE: %d, SKIP: %d\n"
 msgstr ""
 "j - sonraki karar verilmemiş parçaya git, bitince başa dön\n"
 "J - sonraki parçaya git, bitince başa dön\n"
@@ -651,12 +655,30 @@
 "s - geçerli parçayı daha ufak parçalara böl\n"
 "e - geçerli parçayı elle düzenle\n"
 "p - geçerli parçayı yazdır\n"
-"P - geçerli sayfayı sayfalayıcıyı kullanarak yazdır\n"
+"P - geçerli parçayı sayfalayıcıyı kullanarak yazdır\n"
+"> - sonraki dosyaya git, en alttan başa dön\n"
+"< - önceki dosyaya git, en üstten başa dön\n"
 "? - yardımı yazdır\n"
+"PARÇALAR ÖZETİ - Parçalar: %d, KULLAN: %d, ATLA: %d\n"
+
+msgid "'git apply' failed"
+msgstr "\"git apply\" başarısız oldu"
+
+msgid " (was: y)"
+msgstr " (eskisi: y)"
+
+msgid " (was: n)"
+msgstr " (eskisi: n)"
 
 #, c-format
 msgid "Only one letter is expected, got '%s'"
-msgstr "Yalnızca bir harf bekleniyordu, '%s' alındı"
+msgstr "Yalnızca bir harf bekleniyordu, \"%s\" alındı"
+
+msgid "No next file"
+msgstr "Sonrasında dosya yok"
+
+msgid "No previous file"
+msgstr "Öncesinde dosya yok"
 
 msgid "No other hunk"
 msgstr "Başka parça yok"
@@ -710,9 +732,6 @@
 msgid "Unknown command '%s' (use '?' for help)"
 msgstr "Bilinmeyen komut '%s' (yardım için '?')"
 
-msgid "'git apply' failed"
-msgstr "'git apply' başarısız oldu"
-
 msgid "No changes."
 msgstr "Değişiklik yok."
 
@@ -720,6 +739,25 @@
 msgstr "Yalnızca ikili dosyalar değiştirildi."
 
 #, c-format
+msgid "Stage mode change [y,n,q,a,d%s,?]? "
+msgstr "Kip değişimi hazırlansın mı [y,n,q,a,d%s,?]? "
+
+#, c-format
+msgid "Stage deletion [y,n,q,a,d%s,?]? "
+msgstr "Silme hazırlansın mı [y,n,q,a,d%s,?]? "
+
+#, c-format
+msgid "Stage addition [y,n,q,a,d%s,?]? "
+msgstr "Ekleme hazırlansın mı [y,n,q,a,d%s,?]? "
+
+#, c-format
+msgid "Stage this hunk [y,n,q,a,d%s,?]? "
+msgstr "Bu parça hazırlansın mı [y,n,q,a,d%s,?]? "
+
+msgid "Revision does not refer to a commit"
+msgstr "Revizyon, bir işlemeye başvurmuyor"
+
+#, c-format
 msgid ""
 "\n"
 "Disable this message with \"git config set advice.%s false\""
@@ -910,8 +948,13 @@
 msgstr "düzenli ifade girdi için %d döndürdü: %s"
 
 #, c-format
-msgid "unable to find filename in patch at line %d"
-msgstr "yamanın %d. satırında dosya adı bulunamıyor"
+msgid "unable to find filename in patch at %s:%d"
+msgstr "yamada %s:%d konumunda dosya adı bulunamıyor"
+
+#, c-format
+msgid "git apply: bad git-diff - expected /dev/null, got %s at %s:%d"
+msgstr ""
+"git apply: hatalı git-diff - /dev/null bekleniyordu, %s alındı, konum: %s:%d"
 
 #, c-format
 msgid "git apply: bad git-diff - expected /dev/null, got %s on line %d"
@@ -919,6 +962,14 @@
 "git apply: hatalı git-diff - /dev/null bekleniyordu, %s alındı, satır %d"
 
 #, c-format
+msgid "git apply: bad git-diff - inconsistent new filename at %s:%d"
+msgstr "git apply: hatalı git-diff - %s:%d konumunda tutarsız yeni dosya adı"
+
+#, c-format
+msgid "git apply: bad git-diff - inconsistent old filename at %s:%d"
+msgstr "git apply: hatalı git-diff - %s:%d konumunda tutarsız eski dosya adı"
+
+#, c-format
 msgid "git apply: bad git-diff - inconsistent new filename on line %d"
 msgstr "git apply: hatalı git-diff - %d. satırda tutarsız yeni dosya adı"
 
@@ -927,10 +978,18 @@
 msgstr "git apply: hatalı git-diff - %d. satırda tutarsız eski dosya adı"
 
 #, c-format
+msgid "git apply: bad git-diff - expected /dev/null at %s:%d"
+msgstr "git apply: hatalı git-diff - %s:%d konumunda /dev/null bekleniyordu"
+
+#, c-format
 msgid "git apply: bad git-diff - expected /dev/null on line %d"
 msgstr "git apply: hatalı git-diff - %d. satırda /dev/null bekleniyordu"
 
 #, c-format
+msgid "invalid mode at %s:%d: %s"
+msgstr "%s:%d konumunda geçersiz kip: %s"
+
+#, c-format
 msgid "invalid mode on line %d: %s"
 msgstr "%d. satırda geçersiz kip: %s"
 
@@ -941,6 +1000,20 @@
 #, c-format
 msgid ""
 "git diff header lacks filename information when removing %d leading pathname "
+"component at %s:%d"
+msgid_plural ""
+"git diff header lacks filename information when removing %d leading pathname "
+"components at %s:%d"
+msgstr[0] ""
+"%d öncü yol adı bileşeni kaldırılırken git diff üstbilgisi dosya adı "
+"bilgisine iye değil (konum: %s:%d)"
+msgstr[1] ""
+"%d öncü yol adı bileşeni kaldırılırken git diff üstbilgisi dosya adı "
+"bilgisine iye değil (konum: %s:%d)"
+
+#, c-format
+msgid ""
+"git diff header lacks filename information when removing %d leading pathname "
 "component (line %d)"
 msgid_plural ""
 "git diff header lacks filename information when removing %d leading pathname "
@@ -953,6 +1026,10 @@
 "bilgisine iye değil (%d. satır)"
 
 #, c-format
+msgid "git diff header lacks filename information at %s:%d"
+msgstr "git diff üstbilgisi dosya adı bilgisine iye değil (konum: %s:%d)"
+
+#, c-format
 msgid "git diff header lacks filename information (line %d)"
 msgstr "git diff üstbilgisi dosya adı bilgisine iye değil (%d. satır)"
 
@@ -961,8 +1038,8 @@
 msgstr "recount: beklenmedik satır: %.*s"
 
 #, c-format
-msgid "patch fragment without header at line %d: %.*s"
-msgstr "%d. satırda üstbilgisi olmayan yama parçacığı: %.*s"
+msgid "patch fragment without header at %s:%d: %.*s"
+msgstr "%s:%d konumunda üstbilgisi olmayan yama parçacığı: %.*s"
 
 msgid "new file depends on old contents"
 msgstr "yeni dosyanın eski içeriğe bağımlılığı var"
@@ -971,8 +1048,8 @@
 msgstr "silinen dosya içinde hâlâ bir şeyler var"
 
 #, c-format
-msgid "corrupt patch at line %d"
-msgstr "hasarlı yama, %d. satır"
+msgid "corrupt patch at %s:%d"
+msgstr "%s:%d konumunda hasarlı yama"
 
 #, c-format
 msgid "new file %s depends on old contents"
@@ -987,16 +1064,16 @@
 msgstr "** uyarı: %s dosyası boş duruma gelir; ancak silinmez"
 
 #, c-format
-msgid "corrupt binary patch at line %d: %.*s"
-msgstr "hasarlı ikili yama, %d. satır: %.*s"
+msgid "corrupt binary patch at %s:%d: %.*s"
+msgstr "%s:%d konumunda hasarlı ikili yama: %.*s"
 
 #, c-format
-msgid "unrecognized binary patch at line %d"
-msgstr "tanımlanamayan ikili yama, %d. satır"
+msgid "unrecognized binary patch at %s:%d"
+msgstr "%s:%d konumunda tanımlanamayan ikili yama"
 
 #, c-format
-msgid "patch with only garbage at line %d"
-msgstr "yalnızca anlamsız veri içeren yama, %d. satır"
+msgid "patch with only garbage at %s:%d"
+msgstr "%s:%d konumunda yalnızca anlamsız veri içeren yama"
 
 #, c-format
 msgid "unable to read symlink %s"
@@ -1232,7 +1309,7 @@
 
 #, c-format
 msgid "cannot unlink '%s'"
-msgstr "'%s' bağlantısı kesilemiyor"
+msgstr "\"%s\" bağlantısı kesilemiyor"
 
 #, c-format
 msgid "Hunk #%d applied cleanly."
@@ -1244,7 +1321,7 @@
 
 #, c-format
 msgid "Skipped patch '%s'."
-msgstr "'%s' yaması atlandı."
+msgstr "\"%s\" yaması atlandı."
 
 msgid "No valid patches in input (allow with \"--allow-empty\")"
 msgstr "Girdide geçerli yama yok (\"--allow-empty\" ile izin ver)"
@@ -1253,8 +1330,16 @@
 msgstr "indeks dosyası okunamıyor"
 
 #, c-format
+msgid "option -p expects a non-negative integer, got '%s'"
+msgstr "-p seçeneği negatif olmayan bir tamsayı bekliyordu, \"%s\" alındı"
+
+#, c-format
+msgid "unable to normalize directory: '%s'"
+msgstr "dizin normalleştirilemiyor: \"%s\""
+
+#, c-format
 msgid "can't open patch '%s': %s"
-msgstr "'%s' yaması açılamıyor: %s"
+msgstr "\"%s\" yaması açılamıyor: %s"
 
 #, c-format
 msgid "squelched %d whitespace error"
@@ -1937,6 +2022,10 @@
 msgid "select hunks interactively"
 msgstr "parçaları etkileşimli olarak seç"
 
+msgid "auto advance to the next file when selecting hunks interactively"
+msgstr ""
+"parçaları etkileşimli olarak seçerken sonraki dosyaya kendiliğinden geç"
+
 msgid "edit current diff and apply"
 msgstr "geçerli diff'i düzenle ve uygula"
 
@@ -2432,6 +2521,10 @@
 msgid "Restrict the missing objects to the current sparse-checkout"
 msgstr "Eksik nesneleri geçerli aralıklı çıkışa sınırla"
 
+#, c-format
+msgid "unrecognized argument: %s"
+msgstr "tanımlanamayan argüman: %s"
+
 msgid ""
 "git bisect start [--term-(bad|new)=<term-new> --term-(good|old)=<term-old>]\n"
 "                 [--no-checkout] [--first-parent] [<bad> [<good>...]] [--] "
@@ -3617,18 +3710,6 @@
 msgid "copy out the files from named stage"
 msgstr "dosyaları adı verilen alandan kopyala"
 
-msgid "git checkout [<options>] <branch>"
-msgstr "git checkout [<seçenekler>] <dal>"
-
-msgid "git checkout [<options>] [<branch>] -- <file>..."
-msgstr "git checkout [<seçenekler>] [<dal>] -- <dosya>..."
-
-msgid "git switch [<options>] [<branch>]"
-msgstr "git switch [<seçenekler>] [<dal>]"
-
-msgid "git restore [<options>] [--source=<branch>] <file>..."
-msgstr "git restore [<seçenekler>] [--source=<dal>] <dosya>..."
-
 #, c-format
 msgid "path '%s' does not have our version"
 msgstr "'%s' yolu bizdeki sürüme iye değil"
@@ -3813,28 +3894,31 @@
 "'%s' hem bir yerel dosya hem de bir izleme dalı olabilir.\n"
 "-- (ve isteğe bağlı olarak --no-guess) kullanıp belirsizliği giderin"
 
+#, c-format
 msgid ""
 "If you meant to check out a remote tracking branch on, e.g. 'origin',\n"
 "you can do so by fully qualifying the name with the --track option:\n"
 "\n"
-"    git checkout --track origin/<name>\n"
+"    git %s --track origin/<name>\n"
 "\n"
 "If you'd like to always have checkouts of an ambiguous <name> prefer\n"
 "one remote, e.g. the 'origin' remote, consider setting\n"
 "checkout.defaultRemote=origin in your config."
 msgstr ""
-"Eğer bir uzak izleme dalını çıkış yapmak istediyseniz örn. 'origin',\n"
-"bunu adı --track seçeneği ile tam olarak nitelendirip yapabilirsiniz.\n"
+"Eğer bir uzak izleme dalını çıkış yapmak istediyseniz;\n"
+"örn., \"origin\", bunu adı --track seçeneği ile tam\n"
+"olarak nitelendirip yapabilirsiniz.\n"
 "\n"
-"\tgit checkout --track origin/<ad>\n"
+"\tgit %s --track origin/<ad>\n"
 "\n"
-"Eğer her zaman belirsiz <ad> çıkışlarının bir uzak konumu tercih etmesini\n"
-"isterseniz, örn. 'origin', yapılandırmanızda checkout.defaultRemote=origin\n"
+"Eğer her zaman belirsiz <ad> çıkışlarının bir uzak\n"
+"konumu tercih etmesini isterseniz; örn., \"origin\",\n"
+"yapılandırmanızda checkout.defaultRemote=origin\n"
 "ayarını yapmayı düşünün."
 
 #, c-format
 msgid "'%s' matched multiple (%d) remote tracking branches"
-msgstr "'%s' birden çok (%d) uzak izleme dalıyla eşleşti"
+msgstr "\"%s\", birden çok (%d) uzak izleme dalıyla eşleşti"
 
 msgid "only one reference expected"
 msgstr "yalnızca bir başvuru bekleniyordu"
@@ -3853,19 +3937,19 @@
 
 #, c-format
 msgid "a branch is expected, got tag '%s'"
-msgstr "bir dal bekleniyordu, '%s' etiketi alındı"
+msgstr "bir dal bekleniyordu, \"%s\" etiketi alındı"
 
 #, c-format
 msgid "a branch is expected, got remote branch '%s'"
-msgstr "bir dal bekleniyordu, '%s' uzak dalı alındı"
+msgstr "bir dal bekleniyordu, \"%s\" uzak dalı alındı"
 
 #, c-format
 msgid "a branch is expected, got '%s'"
-msgstr "bir dal bekleniyordu, '%s' alındı"
+msgstr "bir dal bekleniyordu, \"%s\" alındı"
 
 #, c-format
 msgid "a branch is expected, got commit '%s'"
-msgstr "bir dal bekleniyordu, '%s' işlemesi alındı"
+msgstr "bir dal bekleniyordu, \"%s\" işlemesi alındı"
 
 msgid ""
 "If you want to detach HEAD at the commit, try again with the --detach option."
@@ -3976,6 +4060,18 @@
 msgid "do not limit pathspecs to sparse entries only"
 msgstr "yol belirteçlerini yalnızca aralıklı girdilere kısıtlama"
 
+msgid "git checkout [<options>] <branch>"
+msgstr "git checkout [<seçenekler>] <dal>"
+
+msgid "git checkout [<options>] [<branch>] -- <file>..."
+msgstr "git checkout [<seçenekler>] [<dal>] -- <dosya>..."
+
+msgid "git switch [<options>] [<branch>]"
+msgstr "git switch [<seçenekler>] [<dal>]"
+
+msgid "git restore [<options>] [--source=<branch>] <file>..."
+msgstr "git restore [<seçenekler>] [--source=<dal>] <dosya>..."
+
 #, c-format
 msgid "options '-%c', '-%c', and '%s' cannot be used together"
 msgstr "'-%c', '-%c' ve '%s' seçenekleri birlikte kullanılamaz"
@@ -5764,10 +5860,6 @@
 msgid "git diff-pairs -z [<diff-options>]"
 msgstr "git diff-pairs -z [<diff-seçenekleri>]"
 
-#, c-format
-msgid "unrecognized argument: %s"
-msgstr "tanımlanamayan argüman: %s"
-
 msgid "working without -z is not supported"
 msgstr "-z olmadan çalışma desteklenmiyor"
 
@@ -5989,13 +6081,6 @@
 "imzalanmış %s işlemesine denk gelindi; işlemek için --signed-commits=<kip> "
 "kullanın"
 
-msgid ""
-"'strip-if-invalid' is not a valid mode for git fast-export with --signed-"
-"commits=<mode>"
-msgstr ""
-"\"strip-if-invalid\", git fast-export için --signed-commits=<kip> ile "
-"geçerli bir kip değil"
-
 #, c-format
 msgid ""
 "omitting tag %s,\n"
@@ -6022,13 +6107,6 @@
 "imzalanmış %s etiketinee denk gelindi; işlemek için --signed-tags=<kip> "
 "kullanın"
 
-msgid ""
-"'strip-if-invalid' is not a valid mode for git fast-export with --signed-"
-"tags=<mode>"
-msgstr ""
-"\"strip-if-invalid\", git fast-export için --signed-tags=<kip> ile geçerli "
-"bir kip değil"
-
 #, c-format
 msgid ""
 "tag %s tags unexported object; use --tag-of-filtered-object=<mode> to handle "
@@ -6446,6 +6524,39 @@
 "işleme için olan geçersiz imza soyuluyor;\n"
 "  görünüşe göre %s tarafından"
 
+#, c-format
+msgid ""
+"replacing invalid signature for commit '%.100s...'\n"
+"  allegedly by %s"
+msgstr ""
+"\"%.100s...\" işlemesi için olan geçersiz imza değiştiriliyor;\n"
+"  görünüşe göre %s tarafından"
+
+#, c-format
+msgid ""
+"replacing invalid signature for commit '%.*s'\n"
+"  allegedly by %s"
+msgstr ""
+"\"%.*s\" işlemesi için olan geçersiz imza değiştiriliyor;\n"
+"  görünüşe göre %s tarafından"
+
+#, c-format
+msgid ""
+"replacing invalid signature for commit\n"
+"  allegedly by %s"
+msgstr ""
+"işleme için olan geçersiz imza değiştiriliyor;\n"
+"  görünüşe göre %s tarafından"
+
+msgid "aborting due to invalid signature"
+msgstr "geçersiz imzadan dolayı iptal ediliyor"
+
+msgid "signing commits in interoperability mode is unsupported"
+msgstr "birlikte çalışabilirlik kipinde işlemeleri imzalamak desteklenmiyor"
+
+msgid "failed to sign commit object"
+msgstr "işleme nesnesi imzalanamadı"
+
 msgid "expected committer but didn't get one"
 msgstr "işlemeci bekleniyordu; ancak bir tane bulunamadı"
 
@@ -6459,6 +6570,9 @@
 msgid "importing a commit signature verbatim"
 msgstr "bir işleme imzası olduğu gibi içe aktarılıyor"
 
+msgid "failed to sign tag object"
+msgstr "etiket nesnesi imzalanamadı"
+
 #, c-format
 msgid "importing a tag signature verbatim for tag '%s'"
 msgstr "\"%s\" etiketi için bir etiket imzası olduğu gibi içe aktarılıyor"
@@ -6471,13 +6585,6 @@
 msgstr ""
 "imzalanmış etikete denk gelindi; işlemek için --signed-tags=<kip> kullanın"
 
-msgid ""
-"'strip-if-invalid' is not a valid mode for git fast-import with --signed-"
-"tags=<mode>"
-msgstr ""
-"\"strip-if-invalid\", git fast-import için --signed-tags=<kip> ile geçerli "
-"bir kip değil"
-
 #, c-format
 msgid "expected 'from' command, got '%s'"
 msgstr "\"from\" komutu bekleniyordu, \"%s\" alındı"
@@ -8043,12 +8150,157 @@
 msgid "'git help config' for more information"
 msgstr "ek bilgi için: 'git help config'"
 
-msgid ""
-"git hook run [--ignore-missing] [--to-stdin=<path>] <hook-name> [-- <hook-"
-"args>]"
+msgid "git history reword <commit> [--dry-run] [--update-refs=(branches|head)]"
 msgstr ""
-"git hook run [--ignore-missing] [--to-stdin=<yol>] <kanca-adı> [-- <kanca-"
-"argümanları>]"
+"git history reword <işleme> [--dry-run] [--update-refs=(branches|head)]"
+
+msgid ""
+"git history split <commit> [--dry-run] [--update-refs=(branches|head)] [--] "
+"[<pathspec>...]"
+msgstr ""
+"git history split <işleme> [--dry-run] [--update-refs=(branches|head)] [--]\n"
+"[<yol-belirteci>...]"
+
+#, c-format
+msgid ""
+"Please enter the commit message for the %s changes. Lines starting\n"
+"with '%s' will be ignored, and an empty message aborts the commit.\n"
+msgstr ""
+"Lütfen %s değişiklikleri için bir işleme iletisi girin. \"%s\" ile başlayan\n"
+"satırlar yok sayılacaktır. Boş bir ileti işlemeyi iptal eder.\n"
+
+#, c-format
+msgid "Aborting commit as launching the editor failed.\n"
+msgstr "Düzenleyici başlatılamadığından işleme iptal edildi.\n"
+
+#, c-format
+msgid "unable to parse parent commit %s"
+msgstr "üst işleme %s ayrıştırılamıyor"
+
+#, c-format
+msgid "%s expects one of 'branches' or 'head'"
+msgstr "%s, \"branches\" veya \"head\" arasından birini bekler"
+
+msgid "error preparing revisions"
+msgstr "revizyonlar hazırlanırken hata"
+
+msgid "replaying merge commits is not supported yet!"
+msgstr "birleştirme işlemelerini yeniden oynatma henüz desteklenmiyor!"
+
+msgid "cannot look up HEAD"
+msgstr "HEAD aranamıyor"
+
+msgid "cannot determine descendance"
+msgstr "soy ilişkisi belirlenemiyor"
+
+msgid ""
+"rewritten commit must be an ancestor of HEAD when using --update-refs=head"
+msgstr ""
+"--update-refs=head ile, yeniden yazılan işleme HEAD'in bir atası olmalıdır"
+
+#, c-format
+msgid "failed to begin ref transaction: %s"
+msgstr "başvuru işlemine başlanılamadı: %s"
+
+#, c-format
+msgid "failed to update ref '%s': %s"
+msgstr "\"%s\" başvurusu güncellenemedi: %s"
+
+#, c-format
+msgid "failed to commit ref transaction: %s"
+msgstr "başvuru işlemi işlenemedi: %s"
+
+msgid "control which refs should be updated"
+msgstr "hangi başvuruların güncellenmesi gerektiğini denetle"
+
+msgid "perform a dry-run without updating any refs"
+msgstr "herhangi bir başvuruyu güncellemeden önce prova yap"
+
+msgid "command expects a single revision"
+msgstr "komut tek bir revizyon bekler"
+
+#, c-format
+msgid "commit cannot be found: %s"
+msgstr "işleme bulunamadı: %s"
+
+msgid "failed writing reworded commit"
+msgstr "iletisi değiştirilen işleme yazılamadı"
+
+msgid "failed replaying descendants"
+msgstr "altsoy yeniden oynatılamadı"
+
+msgid "unable to populate index with tree"
+msgstr "indeks, ağaç ile doldurulamıyor"
+
+msgid "unable to acquire index lock"
+msgstr "indeks kilidi alınamıyor"
+
+msgid "failed reading temporary index"
+msgstr "geçici indeks okunamadı"
+
+msgid "failed split tree"
+msgstr "ağaç bölünemedi"
+
+msgid "split commit is empty"
+msgstr "bölünmüş işleme boş"
+
+msgid "split commit tree matches original commit"
+msgstr "bölünmüş işleme ağacı, özgün işleme ile eşleşiyor"
+
+msgid "failed writing first commit"
+msgstr "ilk işleme yazılamadı"
+
+msgid "failed writing second commit"
+msgstr "ikinci işleme yazılamadı"
+
+msgid "control ref update behavior"
+msgstr "başvuru güncelleme davranışını denetle"
+
+msgid "command expects a committish"
+msgstr "komut, bir işlememsi bekler"
+
+msgid "cannot split up merge commit"
+msgstr "birleştirme işlemesi bölünemez"
+
+msgid ""
+"git hook run [--allow-unknown-hook-name] [--ignore-missing] [--to-"
+"stdin=<path>] <hook-name> [-- <hook-args>]"
+msgstr ""
+"git hook run [--allow-unknown-hook-name] [--ignore-missing]\n"
+"[--to-stdin=<yol>] <kanca-adı> [-- <kanca-argümanları>]"
+
+msgid ""
+"git hook list [--allow-unknown-hook-name] [-z] [--show-scope] <hook-name>"
+msgstr ""
+"git hook list [--allow-unknown-hook-name] [-z] [--show-scope] <kanca-adı>"
+
+msgid "use NUL as line terminator"
+msgstr "sonlandırıcı olarak NUL kullan"
+
+msgid "show the config scope that defined each hook"
+msgstr "her kancayı tanımlayan yapılandırma kapsamını göster"
+
+msgid "allow running a hook with a non-native hook name"
+msgstr "kancayı yerel olmayan kanca adıyla çalıştırmaya izin ver"
+
+msgid "you must specify a hook event name to list"
+msgstr "listeye bir kanca olayı adı belirtmelisiniz"
+
+#, c-format
+msgid ""
+"unknown hook event '%s';\n"
+"use --allow-unknown-hook-name to allow non-native hook names"
+msgstr ""
+"Bilinmeyen kanca olayı \"%s\";\n"
+"yerel olmayan kanca adlarına izin vermek\n"
+"için --allow-unknown-hook-name kullanın."
+
+#, c-format
+msgid "no hooks found for event '%s'"
+msgstr "\"%s\" olayı için bulunan kanca yok"
+
+msgid "hook from hookdir"
+msgstr "kanca dizininden kanca"
 
 msgid "silently ignore missing requested <hook-name>"
 msgstr "istenen eksik <kanca-adı> sessizce yok sayılıyor"
@@ -8357,21 +8609,6 @@
 "                       [--parse] [<dosya>...]"
 
 #, c-format
-msgid "could not stat %s"
-msgstr "%s dosya bilgileri alınamadı"
-
-#, c-format
-msgid "file %s is not a regular file"
-msgstr "%s dosyası sıradan bir dosya değil"
-
-#, c-format
-msgid "file %s is not writable by user"
-msgstr "%s dosyası kullanıcı tarafından yazılabilir değil"
-
-msgid "could not open temporary file"
-msgstr "geçici dosya açılamadı"
-
-#, c-format
 msgid "could not read input file '%s'"
 msgstr "'%s' girdi dosyası okunamadı"
 
@@ -8379,6 +8616,10 @@
 msgstr "stdin'den okunamadı"
 
 #, c-format
+msgid "could not write to temporary file '%s'"
+msgstr "\"%s\" geçici dosyasına yazılamadı"
+
+#, c-format
 msgid "could not rename temporary file to %s"
 msgstr "geçici dosya adı %s olarak değiştirilemedi"
 
@@ -8403,8 +8644,8 @@
 msgid "output only the trailers"
 msgstr "yalnızca artbilgileri çıktı ver"
 
-msgid "do not apply trailer.* configuration variables"
-msgstr "trailer.* yapılandırma değişkenlerini uygulama"
+msgid "do not apply trailer.<key-alias> configuration variables"
+msgstr "trailer.<anahtar-arması> yapılandırma değişkenlerini uygulama"
 
 msgid "reformat multiline trailer values as single-line values"
 msgstr "çok satırlı artbilgileri tek satırlı değerler olarak biçimlendir"
@@ -8424,22 +8665,24 @@
 msgid "no input file given for in-place editing"
 msgstr "yerinde düzenleme için girdi dosyası verilmedi"
 
-msgid "last-modified can only operate on one tree at a time"
-msgstr "last-modified yalnızca aynı anda bir ağaç üzerinde işleyebilir"
+msgid "last-modified can only operate on one commit at a time"
+msgstr "last-modified yalnızca aynı anda bir işleme üzerinde işleyebilir"
+
+#, c-format
+msgid "revision argument '%s' is a %s, not a commit-ish"
+msgstr "revizyon argümanı \"%s\" bir %s, işlememsi değil"
 
 #, c-format
 msgid "unknown last-modified argument: %s"
 msgstr "bilinmeyen last-modified argümanı: %s"
 
-msgid "unable to setup last-modified"
-msgstr "last-modified ayarları yapılamıyor"
-
 msgid ""
-"git last-modified [--recursive] [--show-trees] [<revision-range>] [[--] "
-"<path>...]"
+"git last-modified [--recursive] [--show-trees] [--max-depth=<depth>] [-z]\n"
+"                  [<revision-range>] [[--] <pathspec>...]"
 msgstr ""
-"git last-modified [--recursive] [--show-trees] [<revizyon-erimi>]\n"
-"[[--] <yol>...]"
+"git last-modified [--recursive] [--show-trees] [--max-depth=<derinlik>] [-"
+"z]\n"
+"                  [<revizyon-erimi>] [[--] <yol-belirteci>...]"
 
 msgid "recurse into subtrees"
 msgstr "altağaçlara özyinele"
@@ -8447,6 +8690,12 @@
 msgid "show tree entries when recursing into subtrees"
 msgstr "altağaçlara özyinelerken ağaç girdilerini göster"
 
+msgid "maximum tree depth to recurse"
+msgstr "özyinelenecek en çok ağaç derinliği"
+
+msgid "lines are separated with NUL character"
+msgstr "satırlar NUL karakteri ile ayrılır"
+
 msgid "git log [<options>] [<revision-range>] [[--] <path>...]"
 msgstr "git log [<seçenekler>] [<revizyon-erimi>] [[--] <yol>...]"
 
@@ -8521,6 +8770,18 @@
 msgstr "format.headers değere iye değil"
 
 #, c-format
+msgid "bad boolean config value '%s' for '%s'"
+msgstr "hatalı Boole yapılandırma değeri '%s', '%s' için"
+
+#, c-format
+msgid ""
+"'%s' used to accept any value and treat that as 'true'.\n"
+"Now it only accepts boolean values, like what '%s' does.\n"
+msgstr ""
+"\"%s\" herhangi bir değeri kabul edip ona \"true\" gibi davranıyordu.\n"
+"Artık yalnızca Boole değerleri alıyor; aynı \"%s\" ögesinin yaptığı gibi.\n"
+
+#, c-format
 msgid "cannot open patch file %s"
 msgstr "%s yama dosyası okunamıyor"
 
@@ -8532,7 +8793,7 @@
 
 #, c-format
 msgid "unable to read branch description file '%s'"
-msgstr "'%s' dal açıklama dosyası okunamıyor"
+msgstr "\"%s\" dal açıklama dosyası okunamıyor"
 
 msgid "cover letter needs email format"
 msgstr "ön yazı için e-posta biçimi gerekli"
@@ -8541,6 +8802,10 @@
 msgstr "cover-letter dosyası oluşturulamadı"
 
 #, c-format
+msgid "'%s' is not a valid format string"
+msgstr "\"%s\" geçerli bir biçim dizisi değil"
+
+#, c-format
 msgid "insane in-reply-to: %s"
 msgstr "akıl almaz in-reply-to: %s"
 
@@ -8556,7 +8821,7 @@
 
 #, c-format
 msgid "failed to resolve '%s' as a valid ref"
-msgstr "'%s' geçerli bir başvuru olarak çözülemedi"
+msgstr "\"%s\" geçerli bir başvuru olarak çözülemedi"
 
 msgid "could not find exact merge base"
 msgstr "kesin birleştirme temeli bulunamadı"
@@ -8588,7 +8853,7 @@
 
 #, c-format
 msgid "using '%s' as range-diff origin of current series"
-msgstr "geçerli dizinin range-diff kökeni olarak '%s' kullanılıyor"
+msgstr "geçerli dizinin range-diff kökeni olarak \"%s\" kullanılıyor"
 
 msgid "use [PATCH n/m] even with a single patch"
 msgstr "bir yamayla bile olsa [PATCH n/m] kullan"
@@ -8602,6 +8867,12 @@
 msgid "generate a cover letter"
 msgstr "bir ön yazı oluştur"
 
+msgid "format-spec"
+msgstr "format-spec"
+
+msgid "format spec used for the commit list in the cover letter"
+msgstr "ön yazıdaki işleme listesi için kullanılan biçim belirtimi"
+
 msgid "use simple number sequence for output file names"
 msgstr "çıktı dosya adları için yalın sayı dizisi oluştur"
 
@@ -8609,7 +8880,7 @@
 msgstr "sonek"
 
 msgid "use <sfx> instead of '.patch'"
-msgstr "'.patch' yerine <sonek> kullan"
+msgstr "\".patch\" yerine <sonek> kullan"
 
 msgid "start numbering patches at <n> instead of 1"
 msgstr "yamaları 1 yerine <n>'de numaralandırmaya başla"
@@ -9468,11 +9739,20 @@
 msgstr "birden çok ağacın oluşturulmasına izin ver"
 
 msgid ""
-"git multi-pack-index [<options>] write [--preferred-pack=<pack>][--refs-"
-"snapshot=<path>]"
+"git multi-pack-index [<options>] write [--preferred-pack=<pack>]\n"
+"  [--[no-]bitmap] [--[no-]incremental] [--[no-]stdin-packs]\n"
+"  [--refs-snapshot=<path>]"
 msgstr ""
-"git multi-pack-index [<seçenekler>] write [--preferred-pack=<paket>][--refs-"
-"snapshot=<yol>]"
+"git multi-pack-index [<seçenekler>] write [--preferred-pack=<paket>]\n"
+"  [--[no-]bitmap] [--[no-]incremental] [--[no-]stdin-packs]\n"
+"  [--refs-snapshot=<yol>]"
+
+msgid ""
+"git multi-pack-index [<options>] compact [--[no-]incremental]\n"
+"  [--[no-]bitmap] <from> <to>"
+msgstr ""
+"git multi-pack-index [<seçenekler>] compact [--[no-]incremental]\n"
+"  [--[no-]bitmap] <nereden> <nereye>"
 
 msgid "git multi-pack-index [<options>] verify"
 msgstr "git multi-pack-index [<seçenekler>] verify"
@@ -9507,6 +9787,17 @@
 msgid "refs snapshot for selecting bitmap commits"
 msgstr "biteşlem işlemeleri seçmek için başvuruların anlık görüntüsünü al"
 
+#, c-format
+msgid "could not find MIDX: %s"
+msgstr "MIDX bulunamadı: %s"
+
+msgid "MIDX compaction endpoints must be unique"
+msgstr "MIDX sıkıştırma bitiş uçları benzersiz olmalı"
+
+#, c-format
+msgid "MIDX %s must be an ancestor of %s"
+msgstr "MIDX %s, %s ögesinin bir atası olmalı"
+
 msgid ""
 "during repack, collect pack-files of smaller size into a batch that is "
 "larger than this size"
@@ -10116,14 +10407,14 @@
 msgstr "%s nesnesinin türü alınamıyor (%s paketinde)"
 
 #, c-format
-msgid "packfile %s is a promisor but --exclude-promisor-objects was given"
-msgstr "paket dosyası %s bir vaatçi; ancak --exclude-promisor-objects verildi"
-
-#, c-format
 msgid "could not find pack '%s'"
 msgstr "'%s' paketi bulunamadı"
 
 #, c-format
+msgid "packfile %s is a promisor but --exclude-promisor-objects was given"
+msgstr "paket dosyası %s bir vaatçi; ancak --exclude-promisor-objects verildi"
+
+#, c-format
 msgid "packfile %s cannot be accessed"
 msgstr "paket dosyası %s erişilemedi"
 
@@ -10152,9 +10443,6 @@
 "nesne numarası bekleniyordu, anlamsız veri alındı:\n"
 "%s"
 
-msgid "could not load cruft pack .mtimes"
-msgstr "süprüntü paket .mtimes yüklenemedi"
-
 msgid "cannot open pack index"
 msgstr "paket indeksi açılamıyor"
 
@@ -12400,50 +12688,37 @@
 msgstr "-l ile yalnızca bir dizgi verilebilir"
 
 #, c-format
-msgid "'%s' is not a valid commit-ish for %s"
-msgstr "\"%s\", %s için geçerli bir işlememsi değil"
-
-msgid "need some commits to replay"
-msgstr "yeniden oynatmak için birkaç işleme gerekli"
-
-msgid "all positive revisions given must be references"
-msgstr "verilen tüm pozitif revizyonlar, başvuru olmalı"
-
-msgid "argument to --advance must be a reference"
-msgstr "--advance'a olan argüman bir başvuru olmalı"
-
-msgid ""
-"cannot advance target with multiple sources because ordering would be ill-"
-"defined"
-msgstr ""
-"birden çok kaynaklı hedef ilerletilemiyor; çünkü sıralama hatalı tanımlanmış "
-"olurdu"
-
-#, c-format
 msgid "invalid %s value: '%s'"
 msgstr "geçersiz %s değeri: \"%s\""
 
 msgid ""
-"(EXPERIMENTAL!) git replay ([--contained] --onto <newbase> | --advance "
-"<branch>) [--ref-action[=<mode>]] <revision-range>"
+"(EXPERIMENTAL!) git replay ([--contained] --onto=<newbase> | --"
+"advance=<branch> | --revert=<branch>)\n"
+"[--ref=<ref>] [--ref-action=<mode>] <revision-range>"
 msgstr ""
-"(DENEYSEL!) git replay ([--contained] --onto <yeni-temel> | --advance <dal>) "
-"[--ref-action[=<kip>]] <revizyon-erimi>..."
-
-msgid "make replay advance given branch"
-msgstr "verilen dalı önceden yeniden oynat"
-
-msgid "replay onto given commit"
-msgstr "verilen işlemeye yeniden oynat"
+"(DENEYSEL!) git replay ([--contained] --onto=<yeni-taban> | --advance=<dal>\n"
+"| --revert=<dal>) [--ref=<başvuru>] [--ref-action=<kip>] <revizyon-erimi>"
 
 msgid "update all branches that point at commits in <revision-range>"
 msgstr "<revizyon-erimi>'ndeki işlemelere işaret eden tüm dalları güncelle"
 
+msgid "replay onto given commit"
+msgstr "verilen işlemeye yeniden oynat"
+
+msgid "make replay advance given branch"
+msgstr "verilen dalı önceden yeniden oynat"
+
+msgid "revert commits onto given branch"
+msgstr "işlemeleri verilen dala geri al"
+
+msgid "reference to update with result"
+msgstr "güncellemeye sonuçla başvur"
+
 msgid "control ref update behavior (update|print)"
 msgstr "başvuru güncelleme davranışını denetle (update|print)"
 
-msgid "option --onto or --advance is mandatory"
-msgstr "--onto veya --advance seçeneğinin kullanımı zorunlu"
+msgid "exactly one of --onto, --advance, or --revert is required"
+msgstr "tam olarak --onto, --advance veya --revert arasından biri gerekiyor"
 
 #, c-format
 msgid ""
@@ -12454,33 +12729,15 @@
 "seçenekleri geçersiz kılınacak"
 
 #, c-format
-msgid "failed to begin ref transaction: %s"
-msgstr "başvuru işlemine başlanılamadı: %s"
-
-msgid "error preparing revisions"
-msgstr "revizyonlar hazırlanırken hata"
-
-msgid "replaying down from root commit is not supported yet!"
-msgstr "kök işlemeden aşağı yeniden oynatma henüz desteklenmiyor!"
-
-msgid "replaying merge commits is not supported yet!"
-msgstr "birleştirme işlemelerini yeniden oynatma henüz desteklenmiyor!"
-
-#, c-format
-msgid "failed to update ref '%s': %s"
-msgstr "\"%s\" başvurusu güncellenemedi: %s"
-
-#, c-format
-msgid "failed to commit ref transaction: %s"
-msgstr "başvuru işlemi işlenemedi: %s"
-
-#, c-format
 msgid "key '%s' not found"
-msgstr "'%s' anahtarı bulunamadı"
+msgstr "\"%s\" anahtarı bulunamadı"
+
+msgid "--keys can only be used with --format=lines or --format=nul"
+msgstr "--keys yalnızca, --format=lines veya --format=nul ile kullanılabilir"
 
 #, c-format
 msgid "invalid format '%s'"
-msgstr "geçersiz biçim '%s'"
+msgstr "geçersiz biçim \"%s\""
 
 msgid "output format"
 msgstr "çıktı biçimi"
@@ -12491,6 +12748,12 @@
 msgid "print all keys/values"
 msgstr "tüm anahtarları/değerleri yazdır"
 
+msgid "show keys"
+msgstr "anahtarları göster"
+
+msgid "--keys cannot be used with a <key> or --all"
+msgstr "--keys, bir <anahtar> veya --all ile kullanılamaz"
+
 msgid "unsupported output format"
 msgstr "desteklenmeyen çıktı biçimi"
 
@@ -12533,6 +12796,18 @@
 msgid "Disk size"
 msgstr "Disk boyutu"
 
+msgid "Largest objects"
+msgstr "En büyük nesneler"
+
+msgid "Maximum size"
+msgstr "En büyük boyut"
+
+msgid "Maximum parents"
+msgstr "En çok üst öge"
+
+msgid "Maximum entries"
+msgstr "En çok girdi"
+
 msgid "Repository structure"
 msgstr "Depo yapısı"
 
@@ -13108,6 +13383,44 @@
 msgid "Unknown hash algorithm"
 msgstr "bilinmeyen sağlama algoritması '%s'"
 
+msgid "assuming SHA-1; use --object-format to override"
+msgstr "SHA-1 varsayılıyor; geçersiz kılmak için --object-format kullanın"
+
+msgid "unable to read header"
+msgstr "üstbilgi okunamıyor"
+
+msgid "unknown index version"
+msgstr "bilinmeyen indeks sürümü"
+
+msgid "unable to read index"
+msgstr "indeks okunamıyor"
+
+msgid "corrupt index file"
+msgstr "bozulmuş indeks dosyası"
+
+#, c-format
+msgid "unable to read entry %u/%u"
+msgstr "%u/%u girdisi okunamıyor"
+
+#, c-format
+msgid "unable to read sha1 %u/%u"
+msgstr "sha1 %u/%u okunamıyor"
+
+#, c-format
+msgid "unable to read crc %u/%u"
+msgstr "crc %u/%u okunamıyor"
+
+#, c-format
+msgid "unable to read 32b offset %u/%u"
+msgstr "32 b ofset %u/%u okunamıyor"
+
+msgid "inconsistent 64b offset index"
+msgstr "tutarsız 64 b ofset indeksi"
+
+#, c-format
+msgid "unable to read 64b offset %u"
+msgstr "64 b ofset %u okunamıyor"
+
 msgid ""
 "git show-ref [--head] [-d | --dereference]\n"
 "             [-s | --hash[=<n>]] [--abbrev[=<n>]] [--branches] [--tags]\n"
@@ -13355,27 +13668,25 @@
 msgstr "git stash store [(-m | --message) <ileti>] [-q | --quiet] <işleme>"
 
 msgid ""
-"git stash [push [-p | --patch] [-S | --staged] [-k | --[no-]keep-index] [-q "
+"git stash [push] [-p | --patch] [-S | --staged] [-k | --[no-]keep-index] [-q "
 "| --quiet]\n"
 "          [-u | --include-untracked] [-a | --all] [(-m | --message) "
 "<message>]\n"
 "          [--pathspec-from-file=<file> [--pathspec-file-nul]]\n"
-"          [--] [<pathspec>...]]"
+"          [--] [<pathspec>...]"
 msgstr ""
-"git stash [push [-p | --patch] [-S | --staged] [-k | --[no-]keep-index] [-q\n"
-"          | --quiet] [-u | --include-untracked] [-a | --all] [(-m | --"
-"message)\n"
-"          <ileti>] [--pathspec-from-file=<dosya> [--pathspec-file-nul]]\n"
-"          [--] [<yol-blrtç>...]]"
+"git stash [push] [-p | --patch] [-S | --staged] [-k | --[no-]keep-index]\n"
+"[-q | --quiet] [-u | --include-untracked] [-a | --all] [(-m | --message)\n"
+"<ileti>] [--pathspec-from-file=<dosya> [--pathspec-file-nul]] [--]\n"
+"[<yol-belirteci>...]"
 
 msgid ""
 "git stash save [-p | --patch] [-S | --staged] [-k | --[no-]keep-index] [-q | "
 "--quiet]\n"
 "          [-u | --include-untracked] [-a | --all] [<message>]"
 msgstr ""
-"git stash save [-p | --patch] [-S | --staged] [-k | --[no-]keep-index] [-q "
-"|\n"
-"          --quiet] [-u | --include-untracked] [-a | --all] [<ileti>]"
+"git stash save [-p | --patch] [-S | --staged] [-k | --[no-]keep-index]\n"
+"[-q | --quiet] [-u | --include-untracked] [-a | --all] [<ileti>]"
 
 msgid "git stash create [<message>]"
 msgstr "git stash create [<ileti>]"
@@ -13388,7 +13699,7 @@
 
 #, c-format
 msgid "'%s' is not a stash-like commit"
-msgstr "'%s' zulaya benzer bir işleme değil"
+msgstr "\"%s\", zulaya benzer bir işleme değil"
 
 msgid "No stash entries found."
 msgstr "Zula girdisi bulunamadı."
@@ -13629,6 +13940,9 @@
 msgid "could not get a repository handle for submodule '%s'"
 msgstr "'%s' altmodülü için depo tutacağı alınamadı"
 
+msgid "git submodule--helper get-default-remote <path>"
+msgstr "git submodule--helper get-default-remote <yol>"
+
 #, c-format
 msgid "No url found for submodule path '%s' in .gitmodules"
 msgstr ".gitmodules içinde '%s' altmodül yolu için url bulunamadı"
@@ -13664,6 +13978,16 @@
 msgstr "git submodule foreach [--quiet] [--recursive] [--] <komut>"
 
 #, c-format
+msgid ""
+"failed to set a valid default config for 'submodule.%s.gitdir'. Please "
+"ensure it is set, for example by running something like: 'git config "
+"submodule.%s.gitdir .git/modules/%s'"
+msgstr ""
+"\"submodule.%s.gitdir\" için geçerli bir öntanımlı yapılandırma "
+"ayarlanamadı. Lütfen, şunun gibi bir şey çalıştırarak onun ayarlı olduğundan "
+"emin olun: \"git config submodule.%s.gitdir .git/modules/%s\"."
+
+#, c-format
 msgid "Failed to register url for submodule path '%s'"
 msgstr "Altmodül yolu url'si '%s' kaydı yapılamadı"
 
@@ -13748,20 +14072,42 @@
 msgid "could not fetch a revision for HEAD"
 msgstr "HEAD için bir revizyon getirilemedi"
 
+msgid "git submodule--helper gitdir <name>"
+msgstr "git submodule--helper gitdir <ad>"
+
+msgid ""
+"could not set core.repositoryformatversion to 1.\n"
+"Please set it for migration to work, for example:\n"
+"git config core.repositoryformatversion 1"
+msgstr ""
+"core.repositoryformatversion, 1 olarak ayarlanamadı.\n"
+"Geçişin çalışabillmesi için lütfen onu ayarlayın; örn.:\n"
+"git config core.repositoryformatversion 1"
+
+msgid ""
+"could not enable submodulePathConfig extension. It is required\n"
+"for migration to work. Please enable it in the root repo:\n"
+"git config extensions.submodulePathConfig true"
+msgstr ""
+"submodulePathConfig eklentisi etkinleştirilemedi. Göçün\n"
+"gerçekleştirilebilmesi için gereklidir. Lütfen onu kök\n"
+"depoda etkinleştirin:\n"
+"\tgit config extensions.submodulePathConfig true"
+
 #, c-format
 msgid "Synchronizing submodule url for '%s'\n"
-msgstr "'%s' için altmodül url'si eşitleniyor\n"
+msgstr "\"%s\" için altmodül URL'si eşitleniyor\n"
 
 #, c-format
 msgid "failed to register url for submodule path '%s'"
-msgstr "'%s' altmodülü yolu için url kaydı yapılamadı"
+msgstr "\"%s\" altmodülü yolu için URL kaydı yapılamadı"
 
 #, c-format
 msgid "failed to update remote for submodule '%s'"
-msgstr "'%s' altmodülü için uzak konum güncellenemedi"
+msgstr "\"%s\" altmodülü için uzak konum güncellenemedi"
 
 msgid "suppress output of synchronizing submodule url"
-msgstr "altmodül url'si eşitleme çıktısını gizle"
+msgstr "altmodül URL'si eşitleme çıktısını gizle"
 
 msgid "git submodule sync [--quiet] [--recursive] [<path>]"
 msgstr "git submodule sync [--quiet] [--recursive] [<yol>]"
@@ -13771,7 +14117,7 @@
 "Submodule work tree '%s' contains a .git directory. This will be replaced "
 "with a .git file by using absorbgitdirs."
 msgstr ""
-"Altmodül çalışma ağacı '%s' bir .git dizini içeriyor. Bu, absorbgitdirs "
+"Altmodül çalışma ağacı \"%s\" bir .git dizini içeriyor. Bu, absorbgitdirs "
 "kullanılarak bir .git dosyası ile değiştirilecek."
 
 #, c-format
@@ -13842,21 +14188,24 @@
 msgstr "'%s' submodule.alternateLocation değeri tanımlanamadı"
 
 #, c-format
-msgid "refusing to create/use '%s' in another submodule's git dir"
-msgstr ""
-"başka bir altmodülün git dizininde '%s' oluşturma/kullanma reddediliyor"
-
-#, c-format
 msgid "directory not empty: '%s'"
-msgstr "dizin boş değil: '%s'"
+msgstr "dizin boş değil: \"%s\""
 
 #, c-format
 msgid "clone of '%s' into submodule path '%s' failed"
-msgstr "'%s' ögesinin '%s' altmodül yoluna klonlanması başarısız"
+msgstr "\"%s\" ögesinin \"%s\" altmodül yoluna klonlanması başarısız"
+
+#, c-format
+msgid ""
+"refusing to create/use '%s' in another submodule's git dir. Enabling "
+"extensions.submodulePathConfig should fix this."
+msgstr ""
+"Başka bir altmodülün git dizininde \"%s\" oluşturma/kullanma reddediliyor. "
+"extensions.submodulePathConfig'i etkinleştirmek bunu çözecektir."
 
 #, c-format
 msgid "could not get submodule directory for '%s'"
-msgstr "'%s' için altmodül dizini alınamadı"
+msgstr "\"%s\" için altmodül dizini alınamadı"
 
 msgid "alternative anchor for relative paths"
 msgstr "göreceli yollar için alternatif tutturucu"
@@ -14742,12 +15091,12 @@
 msgid "report pruned working trees"
 msgstr "budanan çalışma ağaçlarını bildir"
 
-msgid "expire working trees older than <time>"
-msgstr "<zaman>'dan eski çalışma ağaçlarının hükmünü kaldır"
+msgid "prune missing working trees older than <time>"
+msgstr "<zaman>'dan eski kayıp çalışma ağaçlarını buda"
 
 #, c-format
 msgid "'%s' already exists"
-msgstr "'%s' halihazırda var"
+msgstr "\"%s\" halihazırda var"
 
 #, c-format
 msgid "unusable worktree destination '%s'"
@@ -14791,33 +15140,26 @@
 
 #, c-format
 msgid "could not find created worktree '%s'"
-msgstr "oluşturulan '%s' çalışma ağacı bulunamadı"
+msgstr "oluşturulan \"%s\" çalışma ağacı bulunamadı"
 
 #, c-format
 msgid "Preparing worktree (new branch '%s')"
-msgstr "Çalışma ağacı hazırlanıyor (yeni dal '%s')"
+msgstr "Çalışma ağacı hazırlanıyor (yeni dal \"%s\")"
 
 #, c-format
 msgid "Preparing worktree (resetting branch '%s'; was at %s)"
-msgstr "Çalışma ağacı hazırlanıyor ('%s' dalı sıfırlanıyor; %s konumundaydı)"
+msgstr "Çalışma ağacı hazırlanıyor (\"%s\" dalı sıfırlanıyor; %s konumundaydı)"
 
 #, c-format
 msgid "Preparing worktree (checking out '%s')"
-msgstr "Çalışma ağacı hazırlanıyor ('%s' çıkış yapılıyor)"
+msgstr "Çalışma ağacı hazırlanıyor (\"%s\" çıkış yapılıyor)"
 
 #, c-format
 msgid "Preparing worktree (detached HEAD %s)"
 msgstr "Çalışma ağacı hazırlanıyor (ayrık HEAD %s)"
 
-#, c-format
-msgid ""
-"HEAD points to an invalid (or orphaned) reference.\n"
-"HEAD path: '%s'\n"
-"HEAD contents: '%s'"
-msgstr ""
-"HEAD, geçersiz (veya yetim bırakılmış bir başvuruya işaret ediyor.\n"
-"HEAD yolu: '%s'\n"
-"HEAD içeriği: '%s'"
+msgid "HEAD points to an invalid (or orphaned) reference.\n"
+msgstr "HEAD, geçersiz (veya yetim bırakılmış) bir başvuruya işaret ediyor.\n"
 
 msgid ""
 "No local or remote refs exist despite at least one remote\n"
@@ -14873,8 +15215,9 @@
 msgid "show extended annotations and reasons, if available"
 msgstr "varsa genişletilmiş açıklamaları ve nedenleri göster"
 
-msgid "add 'prunable' annotation to worktrees older than <time>"
-msgstr "<zaman>'dan eski çalışma ağaçlarına 'budanabilir' açıklama ekle"
+msgid "add 'prunable' annotation to missing worktrees older than <time>"
+msgstr ""
+"<zaman>'dan eski kayıp çalışma ağaçlarına \"prunable\" açıklamasını ekle"
 
 msgid "terminate records with a NUL character"
 msgstr "kayıtları bir NUL karakteriyle sonlandır"
@@ -15361,6 +15704,9 @@
 msgid "Display help information about Git"
 msgstr "Git yardım bilgisini görüntüle"
 
+msgid "EXPERIMENTAL: Rewrite history"
+msgstr "DENEYSEL: Geçmişi yeniden yaz"
+
 msgid "Run git hooks"
 msgstr "Git kancalarını çalıştır"
 
@@ -15463,8 +15809,8 @@
 msgid "Pack heads and tags for efficient repository access"
 msgstr "Tez depo erişimi için dal uçları ve etiketler paketle"
 
-msgid "Compute unique ID for a patch"
-msgstr "Bir yama için eşi olmayan numara hesapla"
+msgid "Compute unique IDs for patches"
+msgstr "Yamalar için benzersiz kimlikleri hesapla"
 
 msgid "Prune all unreachable objects from the object database"
 msgstr "Ulaşılamayan tüm nesneleri nesne veritabanından buda"
@@ -15783,7 +16129,7 @@
 
 #, c-format
 msgid "commit-graph version %X does not match version %X"
-msgstr "commit-graph sürümü %x, %X ile eşleşmiyor"
+msgstr "commit-graph sürümü %X, %X ile eşleşmiyor"
 
 #, c-format
 msgid "commit-graph hash version %X does not match version %X"
@@ -16010,6 +16356,10 @@
 msgstr "%s işlemesi ayrıştırılamadı"
 
 #, c-format
+msgid "object %s is a %s, not a %s"
+msgstr "%s nesnesi bir %s, %s değil"
+
+#, c-format
 msgid "%s %s is not a commit!"
 msgstr "%s %s bir işleme değil!"
 
@@ -16413,10 +16763,6 @@
 msgstr "hatalı sayısal yapılandırma değeri '%s', '%s' için (%s içinde): %s"
 
 #, c-format
-msgid "bad boolean config value '%s' for '%s'"
-msgstr "hatalı Boole yapılandırma değeri '%s', '%s' için"
-
-#, c-format
 msgid "failed to expand user dir in: '%s'"
 msgstr "şuradaki kullanıcı dizini genişletilemedi: '%s'"
 
@@ -17091,6 +17437,9 @@
 msgid "unknown value after ws-error-highlight=%.*s"
 msgstr "şundan sonra bilinmeyen değer: ws-error-highlight=%.*s"
 
+msgid "--find-object requires a git repository"
+msgstr "--find-object, bir git deposu gerektirir"
+
 #, c-format
 msgid "unable to resolve '%s'"
 msgstr "'%s' çözülemiyor"
@@ -17466,9 +17815,6 @@
 msgid "<depth>"
 msgstr "<derinlik>"
 
-msgid "maximum tree depth to recurse"
-msgstr "özyinelenecek en çok ağaç derinliği"
-
 msgid "<file>"
 msgstr "<dosya>"
 
@@ -17815,6 +18161,10 @@
 msgid "git fetch-pack: expected response end packet"
 msgstr "git fetch-pack: yanıt sonu paketi bekleniyordu"
 
+#, c-format
+msgid "couldn't resolve 'auto' filter '%s': %s"
+msgstr "şu \"auto\" süzgeci çözülemedi: \"%s\": %s"
+
 msgid "no matching remote head"
 msgstr "eşleşen uzak dal ucu yok"
 
@@ -18227,15 +18577,27 @@
 "The '%s' hook was ignored because it's not set as executable.\n"
 "You can disable this warning with `git config set advice.ignoredHook false`."
 msgstr ""
-"'%s' kancası yok sayıldı; çünkü bir yürütülebilir olarak ayarlanmamış.\n"
-"Bu uyarıyı 'git config set advice.ignoredHook false' ile kapatabilirsiniz."
+"\"%s\" kancası yok sayıldı; çünkü bir yürütülebilir olarak ayarlanmamış.\n"
+"Bu uyarıyı, \"git config set advice.ignoredHook false\" ile kapatabilirsiniz."
+
+#, c-format
+msgid "disabled hook '%s' has no command configured"
+msgstr "devre dışı \"%s\" kancasında yapılandırılmış bir komut yok"
+
+#, c-format
+msgid ""
+"'hook.%s.command' must be configured or 'hook.%s.event' must be removed; "
+"aborting."
+msgstr ""
+"\"hook.%s.command\" yapılandırılmış veya \"hook.%s.event\" kaldırılmış "
+"olmalı; iptal ediliyor."
 
 msgid "not a git repository"
 msgstr "bir git deposu değil"
 
 #, c-format
 msgid "argument to --packfile must be a valid hash (got '%s')"
-msgstr "--packfile için argüman geçerli bir sağlama olmalıdır ('%s' alındı)"
+msgstr "--packfile için argüman geçerli bir sağlama olmalıdır (\"%s\" alındı)"
 
 #, c-format
 msgid "negative value for http.postBuffer; defaulting to %d"
@@ -18282,6 +18644,23 @@
 
 #, c-format
 msgid ""
+"response requested a delay greater than http.maxRetryTime (%ld > %ld seconds)"
+msgstr ""
+"yanıt, http.maxRetryTime'dan daha büyük bir gecikme istedi (%ld > %ld saniye)"
+
+#, c-format
+msgid ""
+"configured http.retryAfter exceeds http.maxRetryTime (%ld > %ld seconds)"
+msgstr ""
+"yapılandırılmış http.retryAfter, http.maxRetryTime'ı geçiyor (%ld > %ld "
+"saniye)"
+
+#, c-format
+msgid "rate limited, waiting %ld seconds before retry"
+msgstr "hız sınırı, yeniden denemeden önce %ld saniye bekleniyor"
+
+#, c-format
+msgid ""
 "number too large to represent as curl_off_t on this platform: %<PRIuMAX>"
 msgstr ""
 "bu platformda curl_off_t olarak temsil etmek için sayı pek büyük: %<PRIuMAX>"
@@ -18364,6 +18743,9 @@
 "Hedef klasörü 'git config imap.folder <klasör>' ile ayarlayın.\n"
 "Örneğin, 'git config imap.folder Taslaklar'."
 
+msgid "'auto' filter not supported by this command"
+msgstr "\"auto\" süzgeci, bu komut tarafından desteklenmiyor"
+
 msgid "expected 'tree:<depth>'"
 msgstr "'tree:<derinlik>' bekleniyordu"
 
@@ -18382,12 +18764,18 @@
 msgid "must escape char in sub-filter-spec: '%c'"
 msgstr "sub-filter-spec içinde kaçış karakteri olmalı: '%c'"
 
+msgid "an 'auto' filter cannot be combined"
+msgstr "bir \"auto\" süzgeci başkasıyla birlikte kullanılamaz"
+
 msgid "expected something after combine:"
 msgstr "birlikte kullanımdan sonra bir şeyler bekleniyordu:"
 
 msgid "multiple filter-specs cannot be combined"
 msgstr "çoklu filter-specs birlikte kullanılamaz"
 
+msgid "an 'auto' filter is incompatible with any other filter"
+msgstr "bir \"auto\" süzgeci, başka süzgeçlerle uyumsuzdur"
+
 msgid "unable to upgrade repository format to support partial clone"
 msgstr "depo biçimi kısımsal klonları desteklemesi için yükseltilemiyor"
 
@@ -18420,26 +18808,48 @@
 msgstr "%s işlemesi için kök ağacı yüklenemiyor"
 
 #, c-format
+msgid "could not write lock pid file '%s'"
+msgstr "kilit pid dosyası \"%s\" yazılamadı"
+
+#, c-format
+msgid "malformed lock pid file '%s'"
+msgstr "hatalı oluşturulmuş kilit pid dosyası \"%s\""
+
+#, c-format
 msgid ""
-"Unable to create '%s.lock': %s.\n"
+"Unable to create '%s': %s.\n"
 "\n"
-"Another git process seems to be running in this repository, e.g.\n"
-"an editor opened by 'git commit'. Please make sure all processes\n"
-"are terminated then try again. If it still fails, a git process\n"
-"may have crashed in this repository earlier:\n"
-"remove the file manually to continue."
 msgstr ""
-"'%s.lock' oluşturulamıyor: %s.\n"
+"\"%s\" oluşturulamıyor: %s.\n"
 "\n"
-"Bu depoda başka bir git işlemi çalışıyor gibi görünüyor, örneğin\n"
-"'git commit' kullanılarak açılmış bir düzenleyici. Lütfen tüm işlemlerin\n"
-"sonlandırıldığından emin olun ve yeniden deneyin. Eğer hâlâ başarısız\n"
-"oluyorsa bir git işlemi bu depo içinde daha önceden çakılmış olabilir:\n"
-"Sürdürmek için dosyayı el ile kaldırın."
+
+#, c-format
+msgid ""
+"Lock may be held by process %<PRIuMAX>; if no git process is running, the "
+"lock file may be stale (PIDs can be reused)"
+msgstr ""
+"Kilit dosyası, %<PRIuMAX> süreci tarafından tutuluyor olabilir; çalışan\n"
+"herhangi bir git süreci yoksa kilit dosyası büyük olasılıkla önceden\n"
+"kalmıştır (PID'ler yeniden kullanılabilir)."
+
+#, c-format
+msgid ""
+"Lock was held by process %<PRIuMAX>, which is no longer running; the lock "
+"file appears to be stale"
+msgstr ""
+"Kilit, artık çalışmayan %<PRIuMAX> süreci tarafından\n"
+"tutuluyordu; kilit dosyası görünüşe göre önceden kalmış."
+
+msgid ""
+"Another git process seems to be running in this repository, or the lock file "
+"may be stale"
+msgstr ""
+"Bu depoda başka bir git süreci çalışıyor gibi\n"
+"görünüyor veya kilit dosyası önceden kalmış."
 
 #, c-format
 msgid "Unable to create '%s.lock': %s"
-msgstr "'%s.lock' oluşturulamıyor: %s"
+msgstr "\"%s.lock\" oluşturulamıyor: %s"
 
 msgid "unable to create temporary object directory"
 msgstr "geçici nesne dizini oluşturulamıyor"
@@ -18745,8 +19155,15 @@
 msgid "malformed line: %s"
 msgstr "hatalı oluşturulmuş satır: %s"
 
-msgid "could not load pack"
-msgstr "paket yüklenemedi"
+#, c-format
+msgid "could not load pack %d"
+msgstr "%d paketi yüklenemedi"
+
+msgid "too many packs, unable to compact"
+msgstr "pek çok paket, sıkıştırılamıyor"
+
+msgid "could not determine preferred pack"
+msgstr "yeğlenen paket algılanamadı"
 
 #, c-format
 msgid "unable to link '%s' to '%s'"
@@ -18756,6 +19173,13 @@
 msgid "failed to clear multi-pack-index at %s"
 msgstr "multi-pack-index %s konumunda temizlenemedi"
 
+#, c-format
+msgid "unknown MIDX version: %d"
+msgstr "bilinmeyen MIDX sürümü: %d"
+
+msgid "cannot perform MIDX compaction with v1 format"
+msgstr "v1 biçimiyle MIDX sıkıştırması gerçekleştirilemiyor"
+
 msgid "ignoring existing multi-pack-index; checksum mismatch"
 msgstr "var olan multi-pack-index yok sayılıyor; sağlama toplamı uyumsuzluğu"
 
@@ -19331,10 +19755,6 @@
 msgstr "geçersiz nesne türü \"%s\""
 
 #, c-format
-msgid "object %s is a %s, not a %s"
-msgstr "%s nesnesi bir %s, %s değil"
-
-#, c-format
 msgid "object %s has unknown type id %d"
 msgstr "%s nesnesi %d bilinmeyen tür numarasına iye"
 
@@ -19343,6 +19763,10 @@
 msgstr "nesne ayrıştırılamıyor: %s"
 
 #, c-format
+msgid "unable to open object stream for %s"
+msgstr "%s için nesne akışı açılamıyor"
+
+#, c-format
 msgid "hash mismatch %s"
 msgstr "sağlama uyuşmazlığı %s"
 
@@ -19358,15 +19782,6 @@
 msgid "%s: ignoring alternate object stores, nesting too deep"
 msgstr "%s: alternatif nesne depoları yok sayılıyor, iç içe geçme pek derin"
 
-msgid "unable to fdopen alternates lockfile"
-msgstr "alternatifler kilit dosyası fdopen yapılamıyor"
-
-msgid "unable to read alternates file"
-msgstr "alternatifler dosyası okunamıyor"
-
-msgid "unable to move new alternates file into place"
-msgstr "yeni alternatifler dosyası yerine taşınamıyor"
-
 #, c-format
 msgid "path '%s' does not exist"
 msgstr "'%s' diye bir yol yok"
@@ -19412,6 +19827,15 @@
 msgid "%s is not a valid '%s' object"
 msgstr "%s geçerli bir '%s' nesnesi değil"
 
+msgid "unable to fdopen alternates lockfile"
+msgstr "alternatifler kilit dosyası fdopen yapılamıyor"
+
+msgid "unable to read alternates file"
+msgstr "alternatifler dosyası okunamıyor"
+
+msgid "unable to move new alternates file into place"
+msgstr "yeni alternatifler dosyası yerine taşınamıyor"
+
 #, c-format
 msgid "duplicate entry when writing bitmap index: %s"
 msgstr "biteşlem indeksi yazılırken yinelenen girdi: %s"
@@ -19621,9 +20045,6 @@
 msgid "multi-pack-index reverse-index chunk is the wrong size"
 msgstr "multi-pack-index reverse-index iri parçası yanlış boyutlu"
 
-msgid "could not determine preferred pack"
-msgstr "yeğlenen paket algılanamadı"
-
 msgid "cannot both write and verify reverse index"
 msgstr "ters indeks dosyası hem yazılıp hem doğrulanamıyor"
 
@@ -19647,6 +20068,10 @@
 msgstr "paket dosyası %s eşlemlenemiyor%s"
 
 #, c-format
+msgid "could not load .mtimes for cruft pack '%s'"
+msgstr "süprüntü paketi \"%s\" için .mtimes yüklenemedi"
+
+#, c-format
 msgid "offset before start of pack index for %s (corrupt index?)"
 msgstr "ofset %s için paket indeksinin başlangıcından önce (hasarlı indeks?)"
 
@@ -19660,15 +20085,15 @@
 
 #, c-format
 msgid "option `%s' expects \"always\", \"auto\", or \"never\""
-msgstr "'%s' seçeneği \"always\", \"auto\" veya \"never\" bekliyor"
+msgstr "\"%s\" seçeneği; \"always\", \"auto\" veya \"never\" bekliyor"
 
 #, c-format
 msgid "malformed object name '%s'"
-msgstr "hatalı oluşturulmuş nesne adı '%s'"
+msgstr "hatalı oluşturulmuş nesne adı \"%s\""
 
 #, c-format
 msgid "option `%s' expects \"%s\" or \"%s\""
-msgstr "'%s' seçeneği \"%s\" veya \"%s\" bekliyor"
+msgstr "\"%s\" seçeneği, \"%s\" veya \"%s\" bekliyor"
 
 #, c-format
 msgid "%s requires a value"
@@ -19990,6 +20415,10 @@
 msgid "unable to parse --pretty format"
 msgstr "--pretty biçimi ayrıştırılamıyor"
 
+#, c-format
+msgid "%s is not supported by this command"
+msgstr "%s, bu komut tarafından desteklenmiyor"
+
 msgid "lazy fetching disabled; some objects may not be available"
 msgstr "gerekince getirme devre dışı; bazı nesneler kullanılamayabilir"
 
@@ -20031,13 +20460,35 @@
 msgstr "sunucu bir ad veya URL olmadan bir vaatçiyi öne sürdü: %s"
 
 #, c-format
+msgid ""
+"Storing new %s from server for remote '%s'.\n"
+"    '%s' -> '%s'\n"
+msgstr ""
+"Şu uzak konumdan yeni %s depolanıyor: \"%s\".\n"
+"\t\"%s\" -> \"%s\"\n"
+
+#, c-format
+msgid "invalid filter '%s' for remote '%s' will not be stored: %s"
+msgstr ""
+"şu uzak konum için olan \"%s\" geçersiz süzgeci depolanmayacak: \"%s\": %s"
+
+#, c-format
+msgid "invalid token '%s' for remote '%s' will not be stored"
+msgstr "şu uzak konum için olan \"%s\" geçersiz jetonu depolanmayacak: \"%s\""
+
+#, c-format
 msgid "unknown '%s' value for '%s' config option"
-msgstr "'%s' yapılandırma seçeneği için bilinmeyen değer: '%s'"
+msgstr "\"%s\" yapılandırma seçeneği için bilinmeyen değer: \"%s\""
 
 #, c-format
 msgid "accepted promisor remote '%s' not found"
 msgstr "kabul edilmiş vaatçi uzak konum '%s' bulunamadı"
 
+#, c-format
+msgid "promisor remote '%s' advertised invalid filter '%s': %s"
+msgstr ""
+"vaatçi uzak konum \"%s\", geçersiz \"%s\" süzgecinin tanıtımını yaptı: %s"
+
 msgid "object-info: expected flush after arguments"
 msgstr "object-info: argümanlardan sonra floş bekleniyordu"
 
@@ -20287,28 +20738,36 @@
 
 #, c-format
 msgid "cannot fix permission bits on '%s'"
-msgstr "'%s' üzerindeki izin bitleri onarılamıyor"
+msgstr "\"%s\" üzerindeki izin bitleri onarılamıyor"
 
 #, c-format
 msgid "%s: cannot drop to stage #0"
 msgstr "%s: #0 numaralı hazırlama alanına bırakılamıyor"
 
 #, c-format
+msgid ""
+"Skipping submodule due to ignore=all: %s\n"
+"Use --force if you really want to add the submodule."
+msgstr ""
+"ignore=all nedeniyle altmodül atlanıyor: %s\n"
+"Altmodülü eklemeyi gerçekten istiyorsanız --force kullanın."
+
+#, c-format
 msgid "unexpected diff status %c"
 msgstr "beklenmedik diff durumu %c"
 
 #, c-format
 msgid "remove '%s'\n"
-msgstr "kaldır: '%s'\n"
+msgstr "kaldır: \"%s\"\n"
 
 msgid ""
 "You can fix this with 'git rebase --edit-todo' and then run 'git rebase --"
 "continue'.\n"
 "Or you can abort the rebase with 'git rebase --abort'.\n"
 msgstr ""
-"Bunu 'git rebase --edit-todo' ile düzeltebilir ve ardından 'git rebase --"
+"Bunu \"git rebase --edit-todo\" ile düzeltebilir ve ardından 'git rebase --"
 "continue' yapabilirsiniz.\n"
-"Yeniden temellendirmeyi durdurmak isterseniz 'git rebase --abort' yapın.\n"
+"Yeniden temellendirmeyi durdurmak isterseniz \"git rebase --abort\" yapın.\n"
 
 #, c-format
 msgid ""
@@ -20646,7 +21105,7 @@
 
 #, c-format
 msgid "option `%s' must point to a commit"
-msgstr "'%s' bir işlemeye işaret etmeli"
+msgstr "\"%s\", bir işlemeye işaret etmeli"
 
 msgid "key"
 msgstr "anahtar"
@@ -20663,7 +21122,13 @@
 
 #, c-format
 msgid "no reflog for '%s'"
-msgstr "'%s' için başvuru günlüğü yok"
+msgstr "\"%s\" için başvuru günlüğü yok"
+
+#, c-format
+msgid "in '%s' phase, update aborted by the reference-transaction hook"
+msgstr ""
+"güncelleme, \"%s\" evresindeyken reference-transaction kancası tarafından "
+"durduruldu"
 
 msgid "Checking references consistency"
 msgstr "Başvuruların tutarlılığı denetleniyor"
@@ -20777,9 +21242,6 @@
 msgid "ref updates forbidden inside quarantine environment"
 msgstr "başvuru güncellemeleri karantina ortamı içinde yasak"
 
-msgid "ref updates aborted by hook"
-msgstr "başvuru güncellemeleri kanca tarafından iptal edildi"
-
 #, c-format
 msgid "'%s' exists; cannot create '%s'"
 msgstr "'%s' mevcut; '%s' oluşturulamıyor"
@@ -20961,23 +21423,32 @@
 
 #, c-format
 msgid "invalid server response; got '%s'"
-msgstr "geçersiz sunucu yanıtı; '%s' alındı"
+msgstr "geçersiz sunucu yanıtı; \"%s\" alındı"
 
 #, c-format
 msgid "repository '%s' not found"
-msgstr "'%s' deposu bulunamadı"
+msgstr "\"%s\" deposu bulunamadı"
 
 #, c-format
 msgid "Authentication failed for '%s'"
-msgstr "'%s' için kimlik doğrulaması başarısız"
+msgstr "\"%s\" için kimlik doğrulaması başarısız"
 
 #, c-format
 msgid "unable to access '%s' with http.pinnedPubkey configuration: %s"
-msgstr "'%s', http.pinnedPubkey yapılandırması ile erişilemiyor: %s"
+msgstr "\"%s\", http.pinnedPubkey yapılandırması ile erişilemiyor: %s"
+
+#, c-format
+msgid "rate limited by '%s', please try again in %ld seconds"
+msgstr ""
+"hız, \"%s\" tarafından sınırlandı; lütfen %ld saniye içinde yeniden deneyin"
+
+#, c-format
+msgid "rate limited by '%s', please try again later"
+msgstr "hız, \"%s\" tarafından sınırlandı; lütfen daha sonra yeniden deneyin"
 
 #, c-format
 msgid "unable to access '%s': %s"
-msgstr "'%s' erişilemiyor: %s"
+msgstr "\"%s\" erişilemiyor: %s"
 
 #, c-format
 msgid "redirecting to %s"
@@ -21221,33 +21692,33 @@
 
 #, c-format
 msgid "no such branch: '%s'"
-msgstr "böyle bir dal yok: '%s'"
+msgstr "böyle bir dal yok: \"%s\""
 
 #, c-format
 msgid "no upstream configured for branch '%s'"
-msgstr "'%s' dalı için üstkaynak yapılandırılmamış"
+msgstr "\"%s\" dalı için üstkaynak yapılandırılmamış"
 
 #, c-format
 msgid "upstream branch '%s' not stored as a remote-tracking branch"
-msgstr "üstkaynak dalı '%s' bir uzak izleme dalı olarak depolanmıyor"
+msgstr "üstkaynak dalı \"%s\" bir uzak izleme dalı olarak depolanmıyor"
 
 #, c-format
 msgid "push destination '%s' on remote '%s' has no local tracking branch"
-msgstr "'%s' itme hedefinin ('%s' uzak konumunda) yerel izleme dalı yok"
+msgstr "\"%s\" itme hedefinin (\"%s\" uzak konumunda) yerel izleme dalı yok"
 
 #, c-format
 msgid "branch '%s' has no remote for pushing"
-msgstr "'%s' dalının itme için uzak konumu yok"
+msgstr "\"%s\" dalının itme için uzak konumu yok"
 
 #, c-format
 msgid "push refspecs for '%s' do not include '%s'"
-msgstr "'%s' için olan başvuru belirteçleri '%s' içermiyor"
+msgstr "\"%s\" için olan başvuru belirteçleri \"%s\" içermiyor"
 
 msgid "push has no destination (push.default is 'nothing')"
-msgstr "itilecek bir hedef yok (push.default: 'nothing')"
+msgstr "itilecek bir hedef yok (push.default: \"nothing\")"
 
 msgid "cannot resolve 'simple' push to a single destination"
-msgstr "tek bir konuma 'simple' itme çözülemiyor"
+msgstr "tek bir konuma \"simple\" itme çözülemiyor"
 
 #, c-format
 msgid "couldn't find remote ref %s"
@@ -21255,22 +21726,23 @@
 
 #, c-format
 msgid "* Ignoring funny ref '%s' locally"
-msgstr "* Eğlenceli başvuru '%s' yerel olarak yok sayılıyor"
+msgstr "* Eğlenceli başvuru \"%s\" yerel olarak yok sayılıyor"
 
 #, c-format
-msgid "Your branch is based on '%s', but the upstream is gone.\n"
-msgstr "Dalınız '%s' temelli; ancak üstkaynak kaybolmuş.\n"
-
-msgid "  (use \"git branch --unset-upstream\" to fixup)\n"
-msgstr "  (düzeltmek için \"git branch --unset-upstream\" kullan)\n"
+msgid ""
+"ignoring value '%s' for status.compareBranches, only @{upstream} and @{push} "
+"are supported"
+msgstr ""
+"status.compareBranches için olan \"%s\" değeri yok sayılıyor; yalnızca "
+"@{upstream} ve @{push} desteklenir"
 
 #, c-format
 msgid "Your branch is up to date with '%s'.\n"
-msgstr "Dalınız '%s' ile güncel.\n"
+msgstr "Dalınız \"%s\" ile güncel.\n"
 
 #, c-format
 msgid "Your branch and '%s' refer to different commits.\n"
-msgstr "Sizin dalınız ve '%s' başka işlemelere başvuruyor.\n"
+msgstr "Sizin dalınız ve \"%s\" başka işlemelere başvuruyor.\n"
 
 #, c-format
 msgid "  (use \"%s\" for details)\n"
@@ -21279,8 +21751,8 @@
 #, c-format
 msgid "Your branch is ahead of '%s' by %d commit.\n"
 msgid_plural "Your branch is ahead of '%s' by %d commits.\n"
-msgstr[0] "Dalınız '%s' dalından %d işleme ileride.\n"
-msgstr[1] "Dalınız '%s' dalından %d işleme ileride.\n"
+msgstr[0] "Dalınız, \"%s\" dalından %d işleme ileride.\n"
+msgstr[1] "Dalınız, \"%s\" dalından %d işleme ileride.\n"
 
 msgid "  (use \"git push\" to publish your local commits)\n"
 msgstr "  (yerel işlemelerinizi yayımlamak için \"git push\" kullanın)\n"
@@ -21316,6 +21788,13 @@
 "kullanın)\n"
 
 #, c-format
+msgid "Your branch is based on '%s', but the upstream is gone.\n"
+msgstr "Dalınız '%s' temelli; ancak üstkaynak kaybolmuş.\n"
+
+msgid "  (use \"git branch --unset-upstream\" to fixup)\n"
+msgstr "  (düzeltmek için \"git branch --unset-upstream\" kullan)\n"
+
+#, c-format
 msgid "cannot parse expected object name '%s'"
 msgstr "beklenen nesne adı '%s' ayrıştırılamıyor"
 
@@ -21390,6 +21869,38 @@
 msgid "replace depth too high for object %s"
 msgstr "%s nesnesi için değiştirme derinliği pek yüksek"
 
+#, c-format
+msgid "'%s' is not a valid commit-ish for %s"
+msgstr "\"%s\", %s için geçerli bir işlememsi değil"
+
+#, c-format
+msgid "argument to %s must be a reference"
+msgstr "%s argümanı bir başvuru olmalı"
+
+#, c-format
+msgid ""
+"'%s' cannot be used with multiple revision ranges because the ordering would "
+"be ill-defined"
+msgstr ""
+"\"%s\", birden çok revizyon erimiyle birlikte kullanılamıyor; çünkü sıralama "
+"hatalı tanımlanmış olurdu"
+
+msgid "need some commits to replay"
+msgstr "yeniden oynatmak için birkaç işleme gerekli"
+
+msgid "all positive revisions given must be references"
+msgstr "verilen tüm pozitif revizyonlar, başvuru olmalı"
+
+msgid "'--ref' cannot be used with multiple revision ranges"
+msgstr "\"--ref\", birden çok revizyon erimiyle kullanılamaz"
+
+#, c-format
+msgid "'%s' is not a valid refname"
+msgstr "\"%s\", geçerli bir başvuru adı değil"
+
+msgid "compatibility hash algorithm support requires Rust"
+msgstr "uyumluluk sağlaması algoritması desteği Rust gerektirir"
+
 msgid "corrupt MERGE_RR"
 msgstr "hasar görmüş MERGE_RR"
 
@@ -22118,10 +22629,6 @@
 msgstr "'%s', geçerli bir etiket değil"
 
 #, c-format
-msgid "'%s' is not a valid refname"
-msgstr "'%s', geçerli bir başvuru adı değil"
-
-#, c-format
 msgid "update-ref requires a fully qualified refname e.g. refs/heads/%s"
 msgstr ""
 "update-ref, tümüyle kalifiye bir başvuru adı gerektiriyor; örn. refs/heads/%s"
@@ -22212,6 +22719,15 @@
 msgid "cannot revert during a cherry-pick."
 msgstr "bir seç-al sırasında geri al yapılamıyor."
 
+msgid "trailers file contains empty line"
+msgstr "artbilgi dosyasında boş satır var"
+
+msgid "trailers file is empty"
+msgstr "artbilgi dosyası boş"
+
+msgid "cannot read trailers files"
+msgstr "artbilgi dosyaları okunamıyor"
+
 msgid "unusable squash-onto"
 msgstr "kullanılabilir olmayan squash-onto"
 
@@ -22694,6 +23210,14 @@
 "\n"
 "\tgit config --global --add safe.directory %s"
 
+#, c-format
+msgid "error reading '%s'"
+msgstr "\"%s\" okunurken hata"
+
+#, c-format
+msgid "not a regular file: '%s'"
+msgstr "Normal bir dosya değil: \"%s\""
+
 msgid "Unable to read current working directory"
 msgstr "Şu anki çalışma dizini okunamıyor"
 
@@ -22719,6 +23243,10 @@
 msgstr "çıplak depo '%s', kullanılamaz (safe.bareRepository '%s')"
 
 #, c-format
+msgid "unknown ref storage format: '%s'"
+msgstr "bilinmeyen başvuru depolama biçimi: \"%s\""
+
+#, c-format
 msgid ""
 "problem with core.sharedRepository filemode value (0%.3o).\n"
 "The owner of files must always have read and write permissions."
@@ -23053,11 +23581,7 @@
 
 #, c-format
 msgid "could not lookup name for submodule '%s'"
-msgstr "'%s' altmodülü adı aranamadı"
-
-#, c-format
-msgid "refusing to move '%s' into an existing git dir"
-msgstr "'%s' ögesini mevcut bir git dizinine taşıma reddediliyor"
+msgstr "\"%s\" altmodülü adı aranamadı"
 
 #, c-format
 msgid ""
@@ -23066,8 +23590,8 @@
 "'%s'\n"
 msgstr ""
 "'%s%s' git dizini göç ettiriliyor:\n"
-"şuradan: '%s'\n"
-"şuraya: '%s'\n"
+"şuradan: \"%s\"\n"
+"şuraya: \"%s\"\n"
 
 msgid "could not start ls-files in .."
 msgstr "ls-files şurada başlatılamadı .."
@@ -23077,8 +23601,32 @@
 msgstr "ls-tree beklenmedik bir biçimde %d kodu ile çıktı"
 
 #, c-format
+msgid ""
+"the 'submodule.%s.gitdir' config does not exist for module '%s'. Please "
+"ensure it is set, for example by running something like: 'git config "
+"submodule.%s.gitdir .git/modules/%s'. For details see the "
+"extensions.submodulePathConfig documentation."
+msgstr ""
+"\"submodule.%s.gitdir\" yapılandırması, \"%s\" modülü için bulunmuyor.\n"
+"Lütfen, \"git config submodule.%s.gitdir .git/modules/%s\n"
+"gibi bir komut çalıştırarak ayarlı olduğundan emin olun. Ayrıntılar\n"
+"için extensions.submodulePathConfig belgelendirmesine bakın."
+
+msgid ""
+"enabling extensions.submodulePathConfig might fix the following error, if "
+"it's not already enabled."
+msgstr ""
+"Daha önceden halihazırda etkin değilse \"extensions.submodulePathConfig\"i "
+"etkinleştirmek, bir sonraki hatayı onarabilir."
+
+#, c-format
+msgid "refusing to create/use '%s' in another submodule's  git dir."
+msgstr ""
+"başka bir altmodülün git dizininde \"%s\" oluşturma/kullanma reddediliyor"
+
+#, c-format
 msgid "failed to lstat '%s'"
-msgstr "'%s', lstat yapılamadı"
+msgstr "\"%s\", lstat yapılamadı"
 
 msgid "no remote configured to get bundle URIs from"
 msgstr "demet URI'lerini almak için bir uzak konum yapılandırılmamış"
@@ -23129,6 +23677,10 @@
 msgid "too many commits marked reachable"
 msgstr "pek fazla işleme ulaşılabilir olarak imlenmiş"
 
+#, c-format
+msgid "could not find MIDX with checksum %s"
+msgstr "%s sağlama toplamıyla MIDX bulunamadı"
+
 msgid "could not determine MIDX preferred pack"
 msgstr "MIDX yeğlenen paketi algılanamadı"
 
@@ -23211,22 +23763,44 @@
 
 #, c-format
 msgid "running trailer command '%s' failed"
-msgstr "'%s' artbilgi komutunu çalıştırma başarısız oldu"
+msgstr "\"%s\" artbilgi komutunu çalıştırma başarısız oldu"
 
 #, c-format
 msgid "unknown value '%s' for key '%s'"
-msgstr "bilinmeyen değer '%s' ('%s' anahtarı için)"
+msgstr "bilinmeyen değer \"%s\" (\"%s\" anahtarı için)"
 
 #, c-format
 msgid "empty trailer token in trailer '%.*s'"
-msgstr "'%.*s' artbilgisi içinde boş artbilgi jetonu"
+msgstr "\"%.*s\" artbilgisi içinde boş artbilgi jetonu"
+
+msgid "empty --trailer argument"
+msgstr "boş --trailer argümanı"
+
+#, c-format
+msgid "invalid trailer '%s': missing key before separator"
+msgstr "geçersiz artbilgi \"%s\": Ayırıcı öncesi eksik anahtar"
+
+#, c-format
+msgid "could not stat %s"
+msgstr "%s dosya bilgileri alınamadı"
+
+#, c-format
+msgid "file %s is not a regular file"
+msgstr "%s dosyası sıradan bir dosya değil"
+
+#, c-format
+msgid "file %s is not writable by user"
+msgstr "%s dosyası kullanıcı tarafından yazılabilir değil"
+
+msgid "could not write to temporary file"
+msgstr "geçici dosyaya yazılamadı"
 
 msgid "full write to remote helper failed"
 msgstr "uzak konum yardımcısına tam yazım başarısız"
 
 #, c-format
 msgid "unable to find remote helper for '%s'"
-msgstr "'%s' için uzak konum yardımcısı bulunamadı"
+msgstr "\"%s\" için uzak konum yardımcısı bulunamadı"
 
 msgid "can't dup helper output fd"
 msgstr "uzak konum yardımcısı çıktısı için dosya açıklayıcısı çoğaltılamıyor"
@@ -24548,8 +25122,14 @@
 msgstr ""
 "Aşağıdaki dosyalar 8 bit; ancak Content-Transfer-Encoding desteklemiyorlar.\n"
 
-msgid "Which 8bit encoding should I declare [UTF-8]? "
-msgstr "Hangi 8 bit kodlamayı beyan etmeliyim [UTF-8]? "
+msgid "Declare which 8bit encoding to use [default: UTF-8]? "
+msgstr "Hangi 8 bit kodlamayı beyan etmeliyim [öntanımlı: UTF-8]? "
+
+#, perl-format
+msgid "'%s' does not appear to be a valid charset name. Use it anyway [y/N]? "
+msgstr ""
+"\"%s\", geçerli bir karakter kümesi adı değil gibi görünüyor. Yine de "
+"kullanılsın mı [y/N]?"
 
 #, perl-format
 msgid ""
@@ -24589,6 +25169,10 @@
 msgid "CA path \"%s\" does not exist"
 msgstr "CA yolu \"%s\" mevcut değil"
 
+#, perl-format
+msgid "Only client key \"%s\" specified"
+msgstr "Yalnızca istemci adı \"%s\" belirtildi"
+
 msgid ""
 "    The Cc list above has been expanded by additional\n"
 "    addresses found in the patch commit message. By default\n"
@@ -24677,31 +25261,31 @@
 
 #, perl-format
 msgid "(mbox) Adding cc: %s from line '%s'\n"
-msgstr "(mbox) Cc: %s, '%s' satırından ekleniyor\n"
+msgstr "(mbox) Cc: %s, \"%s\" satırından ekleniyor\n"
 
 #, perl-format
 msgid "(mbox) Adding to: %s from line '%s'\n"
-msgstr "(mbox) To: %s, '%s' satırından ekleniyor\n"
+msgstr "(mbox) To: %s, \"%s\" satırından ekleniyor\n"
 
 #, perl-format
 msgid "(non-mbox) Adding cc: %s from line '%s'\n"
-msgstr "(non-mbox) Cc: %s, '%s' satırından ekleniyor\n"
+msgstr "(non-mbox) Cc: %s, \"%s\" satırından ekleniyor\n"
 
 #, perl-format
 msgid "(body) Adding cc: %s from line '%s'\n"
-msgstr "(body) Cc: %s, '%s' satırından ekleniyor\n"
+msgstr "(body) Cc: %s, \"%s\" satırından ekleniyor\n"
 
 #, perl-format
 msgid "error: invalid SMTP port '%s'\n"
-msgstr "hata: geçersiz SMTP kapısı '%s'\n"
+msgstr "hata: geçersiz SMTP kapısı \"%s\"\n"
 
 #, perl-format
 msgid "(%s) Could not execute '%s'"
-msgstr "(%s) '%s' yürütülemedi"
+msgstr "(%s) \"%s\" yürütülemedi"
 
 #, perl-format
 msgid "(%s) Malformed output from '%s'"
-msgstr "(%s) '%s' ögesinden hatalı oluşturulmuş çıktı"
+msgstr "(%s) \"%s\" ögesinden hatalı oluşturulmuş çıktı"
 
 #, perl-format
 msgid "(%s) failed to close pipe to '%s'"
@@ -24741,7 +25325,7 @@
 
 #, perl-format
 msgid "Skipping %s with backup suffix '%s'.\n"
-msgstr "%s, yedek sonek '%s' ile atlanıyor.\n"
+msgstr "%s, yedek sonek \"%s\" ile atlanıyor.\n"
 
 #. TRANSLATORS: please keep "[y|N]" as is.
 #, perl-format
diff --git a/po/zh_CN.po b/po/zh_CN.po
index 2e249de..3eaa07c 100644
--- a/po/zh_CN.po
+++ b/po/zh_CN.po
@@ -53,6 +53,7 @@
 #   conflict                         |  冲突
 #   core Git                         |  核心 Git 工具
 #   cover letter                     |  附函
+#   cruft pack                       |  废弃包
 #   DAG                              |  有向无环图
 #   dangling object                  |  悬空对象
 #   detached HEAD                    |  分离头指针
@@ -70,6 +71,7 @@
 #   grafts                           |  (提交)嫁接
 #   hash                             |  哈希值
 #   HEAD                             |  HEAD(头指针,亦即当前分支)
+#   heap                             |  堆
 #   head                             |  头、分支
 #   head ref                         |  分支
 #   header                           |  头信息
@@ -116,6 +118,7 @@
 #   reflog                           |  引用日志
 #   refmap                           |  引用映射
 #   refspec                          |  引用规格
+#   regular file                     |  普通文件
 #   remote                           |  远程,远程仓库
 #   remote-tracking branch           |  远程跟踪分支
 #   replay                           |  重放
@@ -131,6 +134,7 @@
 #   signed tag                       |  签名标签
 #   smart HTTP protocol              |  智能 HTTP 协议
 #   squash                           |  挤压
+#   stack                            |  栈
 #   stage                            |  n. 暂存区(即索引); v. 暂存
 #   stale                            |  过期的
 #   stash                            |  n. 储藏区; v. 储藏
@@ -150,7 +154,7 @@
 #   unreachable object               |  不可达对象
 #   unstage                          |  取消暂存
 #   upstream                         |  上游(分支/仓库)
-#   working tree                     |  工作区
+#   working tree/worktree            |  工作区
 #
 #  Preferred Chinese Terms
 #
@@ -170,7 +174,7 @@
 msgstr ""
 "Project-Id-Version: Git\n"
 "Report-Msgid-Bugs-To: Git Mailing List <git@vger.kernel.org>\n"
-"POT-Creation-Date: 2026-01-29 19:17+0800\n"
+"POT-Creation-Date: 2026-04-10 10:05+0800\n"
 "PO-Revision-Date: 2025-11-16 01:03+0800\n"
 "Last-Translator: Teng Long <dyroneteng@gmail.com>\n"
 "Language-Team: GitHub <https://github.com/dyrone/git/>\n"
@@ -181,10 +185,54 @@
 "Plural-Forms: nplurals=2; plural=(n != 1);\n"
 "X-Generator: Gtranslator 42.0\n"
 
-#: add-interactive.c
-#, c-format
-msgid "%s cannot be negative"
-msgstr "%s 不能为负数"
+#: builtin/fast-import.c
+msgid "aborting due to invalid signature"
+msgstr "由于签名无效而中止"
+
+#: builtin/fast-import.c
+msgid "failed to sign tag object"
+msgstr "无法为标签对象签名"
+
+#: builtin/history.c
+msgid "control which refs should be updated"
+msgstr "控制应更新哪些引用"
+
+#: builtin/history.c
+msgid "control ref update behavior"
+msgstr "控制引用更新行为"
+
+#: builtin/replay.c
+msgid ""
+"(EXPERIMENTAL!) git replay ([--contained] --onto=<newbase> | --"
+"advance=<branch> | --revert=<branch>)\n"
+"[--ref=<ref>] [--ref-action=<mode>] <revision-range>"
+msgstr ""
+"(实验性!)git replay ([--contained] --onto=<新基线> | --advance=<分支> | --"
+"revert=<分支>)\n"
+"[--ref=<引用>] [--ref-action=<模式>] <版本范围>"
+
+#: builtin/replay.c
+msgid "reference to update with result"
+msgstr "要更新结果的引用"
+
+#: builtin/stash.c
+msgid ""
+"git stash [push] [-p | --patch] [-S | --staged] [-k | --[no-]keep-index] [-q "
+"| --quiet]\n"
+"          [-u | --include-untracked] [-a | --all] [(-m | --message) "
+"<message>]\n"
+"          [--pathspec-from-file=<file> [--pathspec-file-nul]]\n"
+"          [--] [<pathspec>...]"
+msgstr ""
+"git stash [push] [-p | --patch] [-S | --staged] [-k | --[no-]keep-index] [-q "
+"| --quiet]\n"
+"          [-u | --include-untracked] [-a | --all] [(-m | --message) <消息>]\n"
+"          [--pathspec-from-file=<文件> [--pathspec-file-nul]]\n"
+"          [--] [<路径规格>...]"
+
+#: replay.c
+msgid "'--ref' cannot be used with multiple revision ranges"
+msgstr "'--ref' 不能与多个版本范围一起使用"
 
 #: add-interactive.c
 #, c-format
@@ -388,23 +436,23 @@
 
 #: add-patch.c
 #, c-format
-msgid "Stage mode change [y,n,q,a,d%s,?]? "
-msgstr "暂存模式变更 [y,n,q,a,d%s,?]? "
+msgid "Stage mode change%s [y,n,q,a,d%s,?]? "
+msgstr "暂存模式变更%s [y,n,q,a,d%s,?]? "
 
 #: add-patch.c
 #, c-format
-msgid "Stage deletion [y,n,q,a,d%s,?]? "
-msgstr "暂存删除变更 [y,n,q,a,d%s,?]? "
+msgid "Stage deletion%s [y,n,q,a,d%s,?]? "
+msgstr "暂存删除%s [y,n,q,a,d%s,?]? "
 
 #: add-patch.c
 #, c-format
-msgid "Stage addition [y,n,q,a,d%s,?]? "
-msgstr "暂存新增变更 [y,n,q,a,d%s,?]? "
+msgid "Stage addition%s [y,n,q,a,d%s,?]? "
+msgstr "暂存新增%s [y,n,q,a,d%s,?]? "
 
 #: add-patch.c
 #, c-format
-msgid "Stage this hunk [y,n,q,a,d%s,?]? "
-msgstr "暂存该块 [y,n,q,a,d%s,?]? "
+msgid "Stage this hunk%s [y,n,q,a,d%s,?]? "
+msgstr "暂存该块%s [y,n,q,a,d%s,?]? "
 
 #: add-patch.c
 msgid ""
@@ -428,23 +476,23 @@
 
 #: add-patch.c
 #, c-format
-msgid "Stash mode change [y,n,q,a,d%s,?]? "
-msgstr "储藏模式变更 [y,n,q,a,d%s,?]? "
+msgid "Stash mode change%s [y,n,q,a,d%s,?]? "
+msgstr "储藏模式变更%s [y,n,q,a,d%s,?]? "
 
 #: add-patch.c
 #, c-format
-msgid "Stash deletion [y,n,q,a,d%s,?]? "
-msgstr "储藏删除变更 [y,n,q,a,d%s,?]? "
+msgid "Stash deletion%s [y,n,q,a,d%s,?]? "
+msgstr "储藏删除%s [y,n,q,a,d%s,?]? "
 
 #: add-patch.c
 #, c-format
-msgid "Stash addition [y,n,q,a,d%s,?]? "
-msgstr "储藏新增变更 [y,n,q,a,d%s,?]? "
+msgid "Stash addition%s [y,n,q,a,d%s,?]? "
+msgstr "储藏新增%s [y,n,q,a,d%s,?]? "
 
 #: add-patch.c
 #, c-format
-msgid "Stash this hunk [y,n,q,a,d%s,?]? "
-msgstr "储藏该块 [y,n,q,a,d%s,?]? "
+msgid "Stash this hunk%s [y,n,q,a,d%s,?]? "
+msgstr "储藏该块%s [y,n,q,a,d%s,?]? "
 
 #: add-patch.c
 msgid ""
@@ -468,23 +516,23 @@
 
 #: add-patch.c
 #, c-format
-msgid "Unstage mode change [y,n,q,a,d%s,?]? "
-msgstr "取消暂存模式变更 [y,n,q,a,d%s,?]? "
+msgid "Unstage mode change%s [y,n,q,a,d%s,?]? "
+msgstr "取消暂存模式变更%s [y,n,q,a,d%s,?]? "
 
 #: add-patch.c
 #, c-format
-msgid "Unstage deletion [y,n,q,a,d%s,?]? "
-msgstr "取消暂存删除变更 [y,n,q,a,d%s,?]? "
+msgid "Unstage deletion%s [y,n,q,a,d%s,?]? "
+msgstr "取消暂存删除%s [y,n,q,a,d%s,?]? "
 
 #: add-patch.c
 #, c-format
-msgid "Unstage addition [y,n,q,a,d%s,?]? "
-msgstr "取消暂存新增变更 [y,n,q,a,d%s,?]? "
+msgid "Unstage addition%s [y,n,q,a,d%s,?]? "
+msgstr "取消暂存新增%s [y,n,q,a,d%s,?]? "
 
 #: add-patch.c
 #, c-format
-msgid "Unstage this hunk [y,n,q,a,d%s,?]? "
-msgstr "取消暂存该块 [y,n,q,a,d%s,?]? "
+msgid "Unstage this hunk%s [y,n,q,a,d%s,?]? "
+msgstr "取消暂存该块%s [y,n,q,a,d%s,?]? "
 
 #: add-patch.c
 msgid ""
@@ -508,23 +556,23 @@
 
 #: add-patch.c
 #, c-format
-msgid "Apply mode change to index [y,n,q,a,d%s,?]? "
-msgstr "将模式变更应用到索引 [y,n,q,a,d%s,?]? "
+msgid "Apply mode change to index%s [y,n,q,a,d%s,?]? "
+msgstr "将模式变更应用到索引%s [y,n,q,a,d%s,?]? "
 
 #: add-patch.c
 #, c-format
-msgid "Apply deletion to index [y,n,q,a,d%s,?]? "
-msgstr "将删除操作应用到索引 [y,n,q,a,d%s,?]? "
+msgid "Apply deletion to index%s [y,n,q,a,d%s,?]? "
+msgstr "将删除应用到索引%s [y,n,q,a,d%s,?]? "
 
 #: add-patch.c
 #, c-format
-msgid "Apply addition to index [y,n,q,a,d%s,?]? "
-msgstr "将添加操作应用到索引 [y,n,q,a,d%s,?]? "
+msgid "Apply addition to index%s [y,n,q,a,d%s,?]? "
+msgstr "将添加应用到索引%s [y,n,q,a,d%s,?]? "
 
 #: add-patch.c
 #, c-format
-msgid "Apply this hunk to index [y,n,q,a,d%s,?]? "
-msgstr "将该块应用到索引 [y,n,q,a,d%s,?]? "
+msgid "Apply this hunk to index%s [y,n,q,a,d%s,?]? "
+msgstr "将该块应用到索引%s [y,n,q,a,d%s,?]? "
 
 #: add-patch.c
 msgid ""
@@ -548,23 +596,23 @@
 
 #: add-patch.c
 #, c-format
-msgid "Discard mode change from worktree [y,n,q,a,d%s,?]? "
-msgstr "从工作区中丢弃模式变更 [y,n,q,a,d%s,?]? "
+msgid "Discard mode change from worktree%s [y,n,q,a,d%s,?]? "
+msgstr "从工作区丢弃模式变更%s [y,n,q,a,d%s,?]? "
 
 #: add-patch.c
 #, c-format
-msgid "Discard deletion from worktree [y,n,q,a,d%s,?]? "
-msgstr "从工作区中丢弃删除变更 [y,n,q,a,d%s,?]? "
+msgid "Discard deletion from worktree%s [y,n,q,a,d%s,?]? "
+msgstr "从工作区丢弃删除%s [y,n,q,a,d%s,?]? "
 
 #: add-patch.c
 #, c-format
-msgid "Discard addition from worktree [y,n,q,a,d%s,?]? "
-msgstr "从工作区中丢弃新增变更 [y,n,q,a,d%s,?]? "
+msgid "Discard addition from worktree%s [y,n,q,a,d%s,?]? "
+msgstr "从工作区丢弃新增%s [y,n,q,a,d%s,?]? "
 
 #: add-patch.c
 #, c-format
-msgid "Discard this hunk from worktree [y,n,q,a,d%s,?]? "
-msgstr "从工作区中丢弃该块 [y,n,q,a,d%s,?]? "
+msgid "Discard this hunk from worktree%s [y,n,q,a,d%s,?]? "
+msgstr "从工作区丢弃该块%s [y,n,q,a,d%s,?]? "
 
 #: add-patch.c
 msgid ""
@@ -588,23 +636,23 @@
 
 #: add-patch.c
 #, c-format
-msgid "Discard mode change from index and worktree [y,n,q,a,d%s,?]? "
-msgstr "从索引和工作区中丢弃模式变更 [y,n,q,a,d%s,?]? "
+msgid "Discard mode change from index and worktree%s [y,n,q,a,d%s,?]? "
+msgstr "从索引和工作区丢弃模式变更%s [y,n,q,a,d%s,?]? "
 
 #: add-patch.c
 #, c-format
-msgid "Discard deletion from index and worktree [y,n,q,a,d%s,?]? "
-msgstr "从索引和工作区中丢弃删除变更 [y,n,q,a,d%s,?]? "
+msgid "Discard deletion from index and worktree%s [y,n,q,a,d%s,?]? "
+msgstr "从索引和工作区丢弃删除%s [y,n,q,a,d%s,?]? "
 
 #: add-patch.c
 #, c-format
-msgid "Discard addition from index and worktree [y,n,q,a,d%s,?]? "
-msgstr "从索引和工作区中丢弃新增变更 [y,n,q,a,d%s,?]? "
+msgid "Discard addition from index and worktree%s [y,n,q,a,d%s,?]? "
+msgstr "从索引和工作区丢弃新增%s [y,n,q,a,d%s,?]? "
 
 #: add-patch.c
 #, c-format
-msgid "Discard this hunk from index and worktree [y,n,q,a,d%s,?]? "
-msgstr "从索引和工作区中丢弃该块 [y,n,q,a,d%s,?]? "
+msgid "Discard this hunk from index and worktree%s [y,n,q,a,d%s,?]? "
+msgstr "从索引和工作区丢弃该块%s [y,n,q,a,d%s,?]? "
 
 #: add-patch.c
 msgid ""
@@ -622,23 +670,23 @@
 
 #: add-patch.c
 #, c-format
-msgid "Apply mode change to index and worktree [y,n,q,a,d%s,?]? "
-msgstr "将模式变更应用到索引和工作区 [y,n,q,a,d%s,?]? "
+msgid "Apply mode change to index and worktree%s [y,n,q,a,d%s,?]? "
+msgstr "将模式变更应用到索引和工作区%s [y,n,q,a,d%s,?]? "
 
 #: add-patch.c
 #, c-format
-msgid "Apply deletion to index and worktree [y,n,q,a,d%s,?]? "
-msgstr "将删除操作应用到索引和工作区 [y,n,q,a,d%s,?]? "
+msgid "Apply deletion to index and worktree%s [y,n,q,a,d%s,?]? "
+msgstr "将删除应用到索引和工作区%s [y,n,q,a,d%s,?]? "
 
 #: add-patch.c
 #, c-format
-msgid "Apply addition to index and worktree [y,n,q,a,d%s,?]? "
-msgstr "将添加操作应用到索引和工作区 [y,n,q,a,d%s,?]? "
+msgid "Apply addition to index and worktree%s [y,n,q,a,d%s,?]? "
+msgstr "将添加应用到索引和工作区%s [y,n,q,a,d%s,?]? "
 
 #: add-patch.c
 #, c-format
-msgid "Apply this hunk to index and worktree [y,n,q,a,d%s,?]? "
-msgstr "将该块应用到索引和工作区 [y,n,q,a,d%s,?]? "
+msgid "Apply this hunk to index and worktree%s [y,n,q,a,d%s,?]? "
+msgstr "将该块应用到索引和工作区%s [y,n,q,a,d%s,?]? "
 
 #: add-patch.c
 msgid ""
@@ -656,23 +704,23 @@
 
 #: add-patch.c
 #, c-format
-msgid "Apply mode change to worktree [y,n,q,a,d%s,?]? "
-msgstr "将模式变更应用到工作区 [y,n,q,a,d%s,?]? "
+msgid "Apply mode change to worktree%s [y,n,q,a,d%s,?]? "
+msgstr "将模式变更应用到工作区%s [y,n,q,a,d%s,?]? "
 
 #: add-patch.c
 #, c-format
-msgid "Apply deletion to worktree [y,n,q,a,d%s,?]? "
-msgstr "将删除操作应用到工作区 [y,n,q,a,d%s,?]? "
+msgid "Apply deletion to worktree%s [y,n,q,a,d%s,?]? "
+msgstr "将删除应用到工作区%s [y,n,q,a,d%s,?]? "
 
 #: add-patch.c
 #, c-format
-msgid "Apply addition to worktree [y,n,q,a,d%s,?]? "
-msgstr "将添加操作应用到工作区 [y,n,q,a,d%s,?]? "
+msgid "Apply addition to worktree%s [y,n,q,a,d%s,?]? "
+msgstr "将添加应用到工作区%s [y,n,q,a,d%s,?]? "
 
 #: add-patch.c
 #, c-format
-msgid "Apply this hunk to worktree [y,n,q,a,d%s,?]? "
-msgstr "将该块应用到工作区 [y,n,q,a,d%s,?]? "
+msgid "Apply this hunk to worktree%s [y,n,q,a,d%s,?]? "
+msgstr "将该块应用到工作区%s [y,n,q,a,d%s,?]? "
 
 #: add-patch.c
 msgid ""
@@ -690,6 +738,11 @@
 
 #: add-patch.c
 #, c-format
+msgid "%s cannot be negative"
+msgstr "%s 不能为负数"
+
+#: add-patch.c
+#, c-format
 msgid "could not parse hunk header '%.*s'"
 msgstr "无法解析数据块头信息 '%.*s'"
 
@@ -796,6 +849,7 @@
 msgstr "未应用。\n"
 
 #: add-patch.c
+#, c-format
 msgid ""
 "j - go to the next undecided hunk, roll over at the bottom\n"
 "J - go to the next hunk, roll over at the bottom\n"
@@ -807,19 +861,37 @@
 "e - manually edit the current hunk\n"
 "p - print the current hunk\n"
 "P - print the current hunk using the pager\n"
+"> - go to the next file, roll over at the bottom\n"
+"< - go to the previous file, roll over at the top\n"
 "? - print help\n"
+"HUNKS SUMMARY - Hunks: %d, USE: %d, SKIP: %d\n"
 msgstr ""
-"j - 跳转至下一个未决定块,在底部翻页\n"
-"J - 跳转至下一个块,在底部翻页\n"
-"k - 跳转至上一个未决定块,在顶部翻页\n"
-"K - 跳转至上一个块,在顶部翻页\n"
+"j - 跳转至下一个未决定块,在底部循环\n"
+"J - 跳转至下一个块,在底部循环\n"
+"k - 跳转至上一个未决定块,在顶部循环\n"
+"K - 跳转至上一个块,在顶部循环\n"
 "g - 选择要跳转的块\n"
-"/ - 查找和给定正则表达式匹配的块\n"
-"s - 拆分当前块为更小的块\n"
+"/ - 查找与给定正则表达式匹配的块\n"
+"s - 将当前块拆成更小的块\n"
 "e - 手动编辑当前块\n"
-"p - 显示当前块\n"
-"P - 使用分页器显示当前块\n"
-"? - 显示帮助\n"
+"p - 打印当前块\n"
+"P - 使用分页器打印当前块\n"
+"> - 跳转至下一个文件,在底部循环\n"
+"< - 跳转至上一个文件,在顶部循环\n"
+"? - 打印帮助\n"
+"补丁块摘要 - 块:%d,采用:%d,跳过:%d\n"
+
+#: add-patch.c
+msgid "'git apply' failed"
+msgstr "'git apply' 失败"
+
+#: add-patch.c
+msgid " (was: y)"
+msgstr "(原为:y)"
+
+#: add-patch.c
+msgid " (was: n)"
+msgstr "(原为:n)"
 
 #: add-patch.c
 #, c-format
@@ -827,6 +899,14 @@
 msgstr "预期只有一个字母,得到 '%s'"
 
 #: add-patch.c
+msgid "No next file"
+msgstr "没有下一个文件"
+
+#: add-patch.c
+msgid "No previous file"
+msgstr "没有上一个文件"
+
+#: add-patch.c
 msgid "No other hunk"
 msgstr "没有其他块"
 
@@ -894,10 +974,6 @@
 msgstr "未知命令 '%s'(使用 '?' 寻求帮助)"
 
 #: add-patch.c
-msgid "'git apply' failed"
-msgstr "'git apply' 失败"
-
-#: add-patch.c
 msgid "No changes."
 msgstr "没有修改。"
 
@@ -905,6 +981,30 @@
 msgid "Only binary files changed."
 msgstr "只有二进制文件被修改。"
 
+#: add-patch.c
+#, c-format
+msgid "Stage mode change [y,n,q,a,d%s,?]? "
+msgstr "暂存模式变更 [y,n,q,a,d%s,?]? "
+
+#: add-patch.c
+#, c-format
+msgid "Stage deletion [y,n,q,a,d%s,?]? "
+msgstr "暂存删除变更 [y,n,q,a,d%s,?]? "
+
+#: add-patch.c
+#, c-format
+msgid "Stage addition [y,n,q,a,d%s,?]? "
+msgstr "暂存新增变更 [y,n,q,a,d%s,?]? "
+
+#: add-patch.c
+#, c-format
+msgid "Stage this hunk [y,n,q,a,d%s,?]? "
+msgstr "暂存该块 [y,n,q,a,d%s,?]? "
+
+#: add-patch.c
+msgid "Revision does not refer to a commit"
+msgstr "该版本并不指向一个提交"
+
 #: advice.c
 #, c-format
 msgid ""
@@ -1130,8 +1230,14 @@
 
 #: apply.c
 #, c-format
-msgid "unable to find filename in patch at line %d"
-msgstr "无法在补丁的第 %d 行找到文件名"
+msgid "unable to find filename in patch at %s:%d"
+msgstr "无法在补丁 %s:%d 处找到文件名"
+
+#: apply.c
+#, c-format
+msgid "git apply: bad git-diff - expected /dev/null, got %s at %s:%d"
+msgstr ""
+"git apply:错误的 git-diff - 期望 /dev/null,但在 %2$s:%3$d 处得到 %1$s"
 
 #: apply.c
 #, c-format
@@ -1140,13 +1246,28 @@
 
 #: apply.c
 #, c-format
+msgid "git apply: bad git-diff - inconsistent new filename at %s:%d"
+msgstr "git apply:错误的 git-diff - 在 %s:%d 处新文件名不一致"
+
+#: apply.c
+#, c-format
+msgid "git apply: bad git-diff - inconsistent old filename at %s:%d"
+msgstr "git apply:错误的 git-diff - 在 %s:%d 处旧文件名不一致"
+
+#: apply.c
+#, c-format
 msgid "git apply: bad git-diff - inconsistent new filename on line %d"
-msgstr "git apply:错误的 git-diff - 第 %d 行上新文件名不一致"
+msgstr "git apply:错误的 git-diff - 第 %d 行处新文件名不一致"
 
 #: apply.c
 #, c-format
 msgid "git apply: bad git-diff - inconsistent old filename on line %d"
-msgstr "git apply:错误的 git-diff - 第 %d 行上旧文件名不一致"
+msgstr "git apply:错误的 git-diff - 第 %d 行处旧文件名不一致"
+
+#: apply.c
+#, c-format
+msgid "git apply: bad git-diff - expected /dev/null at %s:%d"
+msgstr "git apply:错误的 git-diff - 在 %s:%d 处应为 /dev/null"
 
 #: apply.c
 #, c-format
@@ -1155,6 +1276,11 @@
 
 #: apply.c
 #, c-format
+msgid "invalid mode at %s:%d: %s"
+msgstr "在 %s:%d 处模式无效:%s"
+
+#: apply.c
+#, c-format
 msgid "invalid mode on line %d: %s"
 msgstr "第 %d 行包含无效文件模式:%s"
 
@@ -1167,6 +1293,21 @@
 #, c-format
 msgid ""
 "git diff header lacks filename information when removing %d leading pathname "
+"component at %s:%d"
+msgid_plural ""
+"git diff header lacks filename information when removing %d leading pathname "
+"components at %s:%d"
+msgstr[0] ""
+"当在 %2$s:%3$d 处移除前导路径名中的 %1$d 个组成部分时,git diff 的头信息缺少"
+"文件名信息"
+msgstr[1] ""
+"当在 %2$s:%3$d 处移除前导路径名中的 %1$d 个组成部分时,git diff 的头信息缺少"
+"文件名信息"
+
+#: apply.c
+#, c-format
+msgid ""
+"git diff header lacks filename information when removing %d leading pathname "
 "component (line %d)"
 msgid_plural ""
 "git diff header lacks filename information when removing %d leading pathname "
@@ -1176,6 +1317,11 @@
 
 #: apply.c
 #, c-format
+msgid "git diff header lacks filename information at %s:%d"
+msgstr "git diff 头缺少文件名信息(%s:%d)"
+
+#: apply.c
+#, c-format
 msgid "git diff header lacks filename information (line %d)"
 msgstr "git diff 的头信息中缺乏文件名信息(第 %d 行)"
 
@@ -1186,8 +1332,8 @@
 
 #: apply.c
 #, c-format
-msgid "patch fragment without header at line %d: %.*s"
-msgstr "第 %d 行的补丁片段没有头信息:%.*s"
+msgid "patch fragment without header at %s:%d: %.*s"
+msgstr "在 %s:%d 的补丁片段缺少头信息:%.*s"
 
 #: apply.c
 msgid "new file depends on old contents"
@@ -1199,8 +1345,8 @@
 
 #: apply.c
 #, c-format
-msgid "corrupt patch at line %d"
-msgstr "补丁在第 %d 行损坏"
+msgid "corrupt patch at %s:%d"
+msgstr "补丁在 %s:%d 损坏"
 
 #: apply.c
 #, c-format
@@ -1219,18 +1365,18 @@
 
 #: apply.c
 #, c-format
-msgid "corrupt binary patch at line %d: %.*s"
-msgstr "二进制补丁在第 %d 行损坏:%.*s"
+msgid "corrupt binary patch at %s:%d: %.*s"
+msgstr "二进制补丁在 %s:%d 损坏:%.*s"
 
 #: apply.c
 #, c-format
-msgid "unrecognized binary patch at line %d"
-msgstr "未能识别的二进制补丁位于第 %d 行"
+msgid "unrecognized binary patch at %s:%d"
+msgstr "在 %s:%d 处无法识别的二进制补丁"
 
 #: apply.c
 #, c-format
-msgid "patch with only garbage at line %d"
-msgstr "补丁文件的第 %d 行只有垃圾数据"
+msgid "patch with only garbage at %s:%d"
+msgstr "补丁在 %s:%d 处仅有无效内容"
 
 #: apply.c
 #, c-format
@@ -1548,6 +1694,16 @@
 
 #: apply.c
 #, c-format
+msgid "option -p expects a non-negative integer, got '%s'"
+msgstr "选项 -p 需要非负整数,得到的是 '%s'"
+
+#: apply.c
+#, c-format
+msgid "unable to normalize directory: '%s'"
+msgstr "无法规范化目录:'%s'"
+
+#: apply.c
+#, c-format
 msgid "can't open patch '%s': %s"
 msgstr "无法打开补丁 '%s':%s"
 
@@ -1814,7 +1970,7 @@
 #: archive.c
 #, c-format
 msgid "Not a regular file: %s"
-msgstr "不是一个常规文件:%s"
+msgstr "不是一个普通文件:%s"
 
 #: archive.c
 #, c-format
@@ -2389,6 +2545,10 @@
 msgid "select hunks interactively"
 msgstr "交互式挑选数据块"
 
+#: builtin/add.c builtin/checkout.c builtin/reset.c builtin/stash.c
+msgid "auto advance to the next file when selecting hunks interactively"
+msgstr "在交互式选择补丁块时自动前进到下一个文件"
+
 #: builtin/add.c
 msgid "edit current diff and apply"
 msgstr "编辑当前差异并应用"
@@ -2516,7 +2676,7 @@
 msgstr "索引文件损坏"
 
 #: builtin/add.c builtin/am.c builtin/checkout.c builtin/clone.c
-#: builtin/commit.c builtin/stash.c merge.c rerere.c
+#: builtin/commit.c builtin/history.c builtin/stash.c merge.c rerere.c
 msgid "unable to write new index file"
 msgstr "无法写新的索引文件"
 
@@ -2532,7 +2692,7 @@
 msgid "invalid value for '%s': '%s'"
 msgstr "'%s' 的值无效:'%s'"
 
-#: builtin/am.c builtin/commit.c builtin/merge.c sequencer.c
+#: builtin/am.c builtin/commit.c builtin/merge.c sequencer.c trailer.c
 #, c-format
 msgid "could not read '%s'"
 msgstr "无法读取 '%s'"
@@ -2681,7 +2841,7 @@
 msgid "applying to an empty history"
 msgstr "正应用到一个空历史上"
 
-#: builtin/am.c builtin/commit.c builtin/merge.c builtin/replay.c sequencer.c
+#: builtin/am.c builtin/commit.c builtin/merge.c replay.c sequencer.c
 msgid "failed to write commit object"
 msgstr "无法写提交对象"
 
@@ -3008,6 +3168,12 @@
 msgid "Restrict the missing objects to the current sparse-checkout"
 msgstr "将缺少的对象限制在当前的稀疏检出中"
 
+#: builtin/backfill.c builtin/diff-pairs.c builtin/log.c builtin/replay.c
+#: builtin/shortlog.c bundle.c
+#, c-format
+msgid "unrecognized argument: %s"
+msgstr "未识别的参数:%s"
+
 #: builtin/bisect.c
 msgid ""
 "git bisect start [--term-(bad|new)=<term-new> --term-(good|old)=<term-old>]\n"
@@ -3637,12 +3803,12 @@
 #: builtin/branch.c
 #, c-format
 msgid "Deleted remote-tracking branch %s (was %s).\n"
-msgstr "已删除远程跟踪分支 %s(曾为 %s)。\n"
+msgstr "已删除远程跟踪分支 %s(原为 %s)。\n"
 
 #: builtin/branch.c
 #, c-format
 msgid "Deleted branch %s (was %s).\n"
-msgstr "已删除分支 %s(曾为 %s)。\n"
+msgstr "已删除分支 %s(原为 %s)。\n"
 
 #: builtin/branch.c builtin/tag.c
 msgid "unable to parse format string"
@@ -4496,22 +4662,6 @@
 msgstr "从指定暂存区中拷出文件"
 
 #: builtin/checkout.c
-msgid "git checkout [<options>] <branch>"
-msgstr "git checkout [<选项>] <分支>"
-
-#: builtin/checkout.c
-msgid "git checkout [<options>] [<branch>] -- <file>..."
-msgstr "git checkout [<选项>] [<分支>] -- <文件>..."
-
-#: builtin/checkout.c
-msgid "git switch [<options>] [<branch>]"
-msgstr "git switch [<选项>] [<分支>]"
-
-#: builtin/checkout.c
-msgid "git restore [<options>] [--source=<branch>] <file>..."
-msgstr "git restore [<选项>] [--source=<分支>] <文件>..."
-
-#: builtin/checkout.c
 #, c-format
 msgid "path '%s' does not have our version"
 msgstr "路径 '%s' 没有我们的版本"
@@ -4730,23 +4880,24 @@
 "请使用 --(和可选的 --no-guess)来消除歧义"
 
 #: builtin/checkout.c
+#, c-format
 msgid ""
 "If you meant to check out a remote tracking branch on, e.g. 'origin',\n"
 "you can do so by fully qualifying the name with the --track option:\n"
 "\n"
-"    git checkout --track origin/<name>\n"
+"    git %s --track origin/<name>\n"
 "\n"
 "If you'd like to always have checkouts of an ambiguous <name> prefer\n"
 "one remote, e.g. the 'origin' remote, consider setting\n"
 "checkout.defaultRemote=origin in your config."
 msgstr ""
-"如果您想要检出一个远程跟踪分支,例如在 'origin' 上的,您可以使用分支\n"
-"全名和 --track 选项:\n"
+"若您想检出远程跟踪分支(例如在 'origin' 上),可以使用 --track 选项写出完整名"
+"称:\n"
 "\n"
-"    git checkout --track origin/<名称>\n"
+"    git %s --track origin/<名称>\n"
 "\n"
-"如果您希望在检出模糊的简短分支名 <名称> 时总是偏好某个远程(如 'origin'),\n"
-"可以在配置中设置 checkout.defaultRemote=origin。"
+"若希望在检出含义模糊的 <名称> 时总是偏好某个远程(例如 'origin'),\n"
+"可在配置中设置 checkout.defaultRemote=origin。"
 
 #: builtin/checkout.c
 #, c-format
@@ -4928,6 +5079,22 @@
 msgstr "对路径规格不只限于稀疏条目"
 
 #: builtin/checkout.c
+msgid "git checkout [<options>] <branch>"
+msgstr "git checkout [<选项>] <分支>"
+
+#: builtin/checkout.c
+msgid "git checkout [<options>] [<branch>] -- <file>..."
+msgstr "git checkout [<选项>] [<分支>] -- <文件>..."
+
+#: builtin/checkout.c
+msgid "git switch [<options>] [<branch>]"
+msgstr "git switch [<选项>] [<分支>]"
+
+#: builtin/checkout.c
+msgid "git restore [<options>] [--source=<branch>] <file>..."
+msgstr "git restore [<选项>] [--source=<分支>] <文件>..."
+
+#: builtin/checkout.c
 #, c-format
 msgid "options '-%c', '-%c', and '%s' cannot be used together"
 msgstr "选项 '-%c'、'-%c' 和 '%s' 不能同时使用"
@@ -6006,7 +6173,8 @@
 msgid "could not read MERGE_MSG"
 msgstr "无法读取 MERGE_MSG"
 
-#: builtin/commit.c bundle.c rerere.c sequencer.c
+#: builtin/commit.c builtin/history.c builtin/submodule--helper.c bundle.c
+#: rerere.c sequencer.c
 #, c-format
 msgid "could not open '%s'"
 msgstr "无法打开 '%s'"
@@ -6308,11 +6476,11 @@
 msgid "the commit is authored by me now (used with -C/-c/--amend)"
 msgstr "现在将该提交的作者改为我(和 -C/-c/--amend 参数共用)"
 
-#: builtin/commit.c builtin/interpret-trailers.c builtin/tag.c
+#: builtin/commit.c builtin/interpret-trailers.c builtin/rebase.c builtin/tag.c
 msgid "trailer"
 msgstr "尾注"
 
-#: builtin/commit.c builtin/tag.c
+#: builtin/commit.c builtin/rebase.c builtin/tag.c
 msgid "add custom trailer(s)"
 msgstr "添加自定义尾注"
 
@@ -6399,7 +6567,7 @@
 msgid "could not read commit message: %s"
 msgstr "无法读取提交说明:%s"
 
-#: builtin/commit.c
+#: builtin/commit.c builtin/history.c
 #, c-format
 msgid "Aborting commit due to empty commit message.\n"
 msgstr "由于提交说明为空,终止提交。\n"
@@ -7137,12 +7305,6 @@
 msgid "git diff-pairs -z [<diff-options>]"
 msgstr "git diff-pairs -z [<差异选项>]"
 
-#: builtin/diff-pairs.c builtin/log.c builtin/replay.c builtin/shortlog.c
-#: bundle.c
-#, c-format
-msgid "unrecognized argument: %s"
-msgstr "未识别的参数:%s"
-
 #: builtin/diff-pairs.c
 msgid "working without -z is not supported"
 msgstr "不支持在不带 -z 参数的情况下工作"
@@ -7417,14 +7579,6 @@
 msgstr "遇到已签名提交 %s;使用 --signed-commits=<模式> 来处理"
 
 #: builtin/fast-export.c
-msgid ""
-"'strip-if-invalid' is not a valid mode for git fast-export with --signed-"
-"commits=<mode>"
-msgstr ""
-"在 git fast-export 的 --signed-commits=<模式> 中,'strip-if-invalid' 不是有效"
-"模式"
-
-#: builtin/fast-export.c
 #, c-format
 msgid ""
 "omitting tag %s,\n"
@@ -7454,14 +7608,6 @@
 msgstr "遇到已签名标签 %s;使用 --signed-tags=<模式> 来处理"
 
 #: builtin/fast-export.c
-msgid ""
-"'strip-if-invalid' is not a valid mode for git fast-export with --signed-"
-"tags=<mode>"
-msgstr ""
-"在 git fast-export 的 --signed-tags=<模式> 中,'strip-if-invalid' 不是有效模"
-"式"
-
-#: builtin/fast-export.c
 #, c-format
 msgid ""
 "tag %s tags unexported object; use --tag-of-filtered-object=<mode> to handle "
@@ -7983,6 +8129,41 @@
 "  据称由 %s 提供"
 
 #: builtin/fast-import.c
+#, c-format
+msgid ""
+"replacing invalid signature for commit '%.100s...'\n"
+"  allegedly by %s"
+msgstr ""
+"正在替换提交 '%.100s...' 的无效签名\n"
+"  据称由 %s 签署"
+
+#: builtin/fast-import.c
+#, c-format
+msgid ""
+"replacing invalid signature for commit '%.*s'\n"
+"  allegedly by %s"
+msgstr ""
+"正在替换提交 '%.*s' 的无效签名\n"
+"  据称由 %s 签署"
+
+#: builtin/fast-import.c
+#, c-format
+msgid ""
+"replacing invalid signature for commit\n"
+"  allegedly by %s"
+msgstr ""
+"正在替换提交的无效签名\n"
+"  据称由 %s 签署"
+
+#: builtin/fast-import.c
+msgid "signing commits in interoperability mode is unsupported"
+msgstr "互操作模式下不支持为提交签名"
+
+#: builtin/fast-import.c
+msgid "failed to sign commit object"
+msgstr "无法为提交对象签名"
+
+#: builtin/fast-import.c
 msgid "expected committer but didn't get one"
 msgstr "预期提交者但未获得"
 
@@ -8013,14 +8194,6 @@
 msgstr "遇到已签名标签;使用 --signed-tags=<模式> 来处理它"
 
 #: builtin/fast-import.c
-msgid ""
-"'strip-if-invalid' is not a valid mode for git fast-import with --signed-"
-"tags=<mode>"
-msgstr ""
-"在 git fast-import 的 --signed-tags=<模式> 中,'strip-if-invalid' 不是有效模"
-"式"
-
-#: builtin/fast-import.c
 #, c-format
 msgid "expected 'from' command, got '%s'"
 msgstr "预期 'from' 命令,实际:'%s'"
@@ -9207,7 +9380,7 @@
 
 #: builtin/gc.c builtin/repack.c
 msgid "with --cruft, limit the size of new cruft packs"
-msgstr "使用 --cruft,限制新 cruft 包的总大小"
+msgstr "使用 --cruft,限制新废弃包的总大小"
 
 #: builtin/gc.c
 msgid "be more thorough (increased runtime)"
@@ -9966,13 +10139,185 @@
 msgid "'git help config' for more information"
 msgstr "'git help config' 获取更多信息"
 
+#: builtin/history.c
+msgid "git history reword <commit> [--dry-run] [--update-refs=(branches|head)]"
+msgstr "git history reword <提交> [--dry-run] [--update-refs=(branches|head)]"
+
+#: builtin/history.c
+msgid ""
+"git history split <commit> [--dry-run] [--update-refs=(branches|head)] [--] "
+"[<pathspec>...]"
+msgstr ""
+"git history split <提交> [--dry-run] [--update-refs=(branches|head)] [--] [<"
+"路径规格>...]"
+
+#: builtin/history.c
+#, c-format
+msgid ""
+"Please enter the commit message for the %s changes. Lines starting\n"
+"with '%s' will be ignored, and an empty message aborts the commit.\n"
+msgstr ""
+"请输入针对 %s 变更的提交说明。以 '%s' 开头的行将被忽略,空说明将中止提交。\n"
+
+#: builtin/history.c
+#, c-format
+msgid "Aborting commit as launching the editor failed.\n"
+msgstr "启动编辑器失败,正在中止提交。\n"
+
+#: builtin/history.c
+#, c-format
+msgid "unable to parse parent commit %s"
+msgstr "无法解析父提交 %s"
+
+#: builtin/history.c
+#, c-format
+msgid "%s expects one of 'branches' or 'head'"
+msgstr "%s 需要 'branches' 或 'head' 之一"
+
+#: builtin/history.c replay.c
+msgid "error preparing revisions"
+msgstr "准备版本时错误"
+
+#: builtin/history.c replay.c
+msgid "replaying merge commits is not supported yet!"
+msgstr "目前还不支持重放合并提交!"
+
+#: builtin/history.c
+msgid "cannot look up HEAD"
+msgstr "无法查找 HEAD"
+
+#: builtin/history.c
+msgid "cannot determine descendance"
+msgstr "无法确定祖先关系"
+
+#: builtin/history.c
+msgid ""
+"rewritten commit must be an ancestor of HEAD when using --update-refs=head"
+msgstr "使用 --update-refs=head 时,重写后的提交必须是 HEAD 的祖先"
+
+#: builtin/history.c builtin/replay.c
+#, c-format
+msgid "failed to begin ref transaction: %s"
+msgstr "无法开始引用事务:%s"
+
+#: builtin/history.c builtin/replay.c
+#, c-format
+msgid "failed to update ref '%s': %s"
+msgstr "无法更新引用 '%s':%s"
+
+#: builtin/history.c builtin/replay.c
+#, c-format
+msgid "failed to commit ref transaction: %s"
+msgstr "无法提交引用事务:%s"
+
+#: builtin/history.c
+msgid "perform a dry-run without updating any refs"
+msgstr "试运行(不更新任何引用)"
+
+#: builtin/history.c
+msgid "command expects a single revision"
+msgstr "该命令需要一个版本"
+
+#: builtin/history.c
+#, c-format
+msgid "commit cannot be found: %s"
+msgstr "找不到提交:%s"
+
+#: builtin/history.c
+msgid "failed writing reworded commit"
+msgstr "写入重写后的提交失败"
+
+#: builtin/history.c
+msgid "failed replaying descendants"
+msgstr "重放后代提交失败"
+
+#: builtin/history.c
+msgid "unable to populate index with tree"
+msgstr "无法用树填充索引"
+
+#: builtin/history.c
+msgid "unable to acquire index lock"
+msgstr "无法获取索引锁"
+
+#: builtin/history.c
+msgid "failed reading temporary index"
+msgstr "读取临时索引失败"
+
+#: builtin/history.c
+msgid "failed split tree"
+msgstr "拆分树失败"
+
+#: builtin/history.c
+msgid "split commit is empty"
+msgstr "拆分得到的提交为空"
+
+#: builtin/history.c
+msgid "split commit tree matches original commit"
+msgstr "拆分提交的树与原始提交相同"
+
+#: builtin/history.c
+msgid "failed writing first commit"
+msgstr "写入第一个提交失败"
+
+#: builtin/history.c
+msgid "failed writing second commit"
+msgstr "写入第二个提交失败"
+
+#: builtin/history.c
+msgid "command expects a committish"
+msgstr "该命令需要一个提交号"
+
+#: builtin/history.c
+msgid "cannot split up merge commit"
+msgstr "无法拆分合并提交"
+
 #: builtin/hook.c
 msgid ""
-"git hook run [--ignore-missing] [--to-stdin=<path>] <hook-name> [-- <hook-"
-"args>]"
+"git hook run [--allow-unknown-hook-name] [--ignore-missing] [--to-"
+"stdin=<path>] <hook-name> [-- <hook-args>]"
 msgstr ""
-"git hook run [--ignore-missing] [--to-stdin=<路径>] <钩子名称> [-- <钩子参数"
-">]"
+"git hook run [--allow-unknown-hook-name] [--ignore-missing] [--to-stdin=<路径"
+">] <钩子名称> [-- <钩子参数>]"
+
+#: builtin/hook.c
+msgid ""
+"git hook list [--allow-unknown-hook-name] [-z] [--show-scope] <hook-name>"
+msgstr ""
+"git hook list [--allow-unknown-hook-name] [-z] [--show-scope] <钩子名称>"
+
+#: builtin/hook.c
+msgid "use NUL as line terminator"
+msgstr "使用 NUL 作为行终止符"
+
+#: builtin/hook.c
+msgid "show the config scope that defined each hook"
+msgstr "显示定义每个钩子的配置作用域"
+
+#: builtin/hook.c
+msgid "allow running a hook with a non-native hook name"
+msgstr "允许使用非原生钩子名称运行钩子"
+
+#: builtin/hook.c
+msgid "you must specify a hook event name to list"
+msgstr "列出时必须指定钩子事件名称"
+
+#: builtin/hook.c
+#, c-format
+msgid ""
+"unknown hook event '%s';\n"
+"use --allow-unknown-hook-name to allow non-native hook names"
+msgstr ""
+"未知的钩子事件 '%s';\n"
+"请使用 --allow-unknown-hook-name 以允许非原生钩子名称"
+
+#: builtin/hook.c
+#, c-format
+msgid "no hooks found for event '%s'"
+msgstr "未找到事件 '%s' 的钩子"
+
+#: builtin/hook.c
+msgid "hook from hookdir"
+msgstr "来自 hookdir 的钩子"
 
 #: builtin/hook.c
 msgid "silently ignore missing requested <hook-name>"
@@ -10355,25 +10700,6 @@
 "                       [(--trailer (<键|键别名>)[(=|:)<值>])...]\n"
 "                       [--parse] [<文件>...]"
 
-#: builtin/interpret-trailers.c wrapper.c
-#, c-format
-msgid "could not stat %s"
-msgstr "无法对 %s 调用 stat"
-
-#: builtin/interpret-trailers.c
-#, c-format
-msgid "file %s is not a regular file"
-msgstr "文件 %s 不是一个普通文件"
-
-#: builtin/interpret-trailers.c
-#, c-format
-msgid "file %s is not writable by user"
-msgstr "文件 %s 用户不可写"
-
-#: builtin/interpret-trailers.c
-msgid "could not open temporary file"
-msgstr "无法打开临时文件"
-
 #: builtin/interpret-trailers.c
 #, c-format
 msgid "could not read input file '%s'"
@@ -10385,6 +10711,11 @@
 
 #: builtin/interpret-trailers.c
 #, c-format
+msgid "could not write to temporary file '%s'"
+msgstr "无法写入临时文件 '%s'"
+
+#: builtin/interpret-trailers.c trailer.c
+#, c-format
 msgid "could not rename temporary file to %s"
 msgstr "无法重命名临时文件为 %s"
 
@@ -10417,8 +10748,8 @@
 msgstr "只输出尾注"
 
 #: builtin/interpret-trailers.c
-msgid "do not apply trailer.* configuration variables"
-msgstr "不应用 trailer.* 配置变量"
+msgid "do not apply trailer.<key-alias> configuration variables"
+msgstr "不应用 trailer.<键别名> 配置变量"
 
 #: builtin/interpret-trailers.c
 msgid "reformat multiline trailer values as single-line values"
@@ -10445,8 +10776,13 @@
 msgstr "没有给出要原位编辑的文件"
 
 #: builtin/last-modified.c
-msgid "last-modified can only operate on one tree at a time"
-msgstr "last-modified 一次只能在一个树对象上操作"
+msgid "last-modified can only operate on one commit at a time"
+msgstr "last-modified 一次只能处理一个提交"
+
+#: builtin/last-modified.c
+#, c-format
+msgid "revision argument '%s' is a %s, not a commit-ish"
+msgstr "版本参数 '%s' 是 %s,不是提交号"
 
 #: builtin/last-modified.c
 #, c-format
@@ -10454,15 +10790,12 @@
 msgstr "未知的 last-modified 参数:%s"
 
 #: builtin/last-modified.c
-msgid "unable to setup last-modified"
-msgstr "无法设置 last-modified"
-
-#: builtin/last-modified.c
 msgid ""
-"git last-modified [--recursive] [--show-trees] [<revision-range>] [[--] "
-"<path>...]"
+"git last-modified [--recursive] [--show-trees] [--max-depth=<depth>] [-z]\n"
+"                  [<revision-range>] [[--] <pathspec>...]"
 msgstr ""
-"git last-modified [--recursive] [--show-trees] [<版本范围>] [[--] <路径>...]"
+"git last-modified [--recursive] [--show-trees] [--max-depth=<深度>] [-z]\n"
+"                  [<版本范围>] [[--] <路径规格>...]"
 
 #: builtin/last-modified.c builtin/ls-tree.c
 msgid "recurse into subtrees"
@@ -10472,6 +10805,14 @@
 msgid "show tree entries when recursing into subtrees"
 msgstr "递归进入子目录时,显示树的条目"
 
+#: builtin/last-modified.c diff.c
+msgid "maximum tree depth to recurse"
+msgstr "递归的最大树深度"
+
+#: builtin/last-modified.c
+msgid "lines are separated with NUL character"
+msgstr "行以 NUL 字符分隔"
+
 #: builtin/log.c
 msgid "git log [<options>] [<revision-range>] [[--] <path>...]"
 msgstr "git log [<选项>] [<版本范围>] [[--] <路径>...]"
@@ -10559,6 +10900,20 @@
 msgid "format.headers without value"
 msgstr "format.headers 没有值"
 
+#: builtin/log.c config.c
+#, c-format
+msgid "bad boolean config value '%s' for '%s'"
+msgstr "配置变量 '%2$s' 的布尔值 '%1$s' 无效"
+
+#: builtin/log.c
+#, c-format
+msgid ""
+"'%s' used to accept any value and treat that as 'true'.\n"
+"Now it only accepts boolean values, like what '%s' does.\n"
+msgstr ""
+"'%s' 过去接受任意值并将其视为 'true'。\n"
+"现在只接受布尔值,行为与 '%s' 相同。\n"
+
 #: builtin/log.c
 #, c-format
 msgid "cannot open patch file %s"
@@ -10587,6 +10942,11 @@
 
 #: builtin/log.c
 #, c-format
+msgid "'%s' is not a valid format string"
+msgstr "'%s' 不是有效的格式字符串"
+
+#: builtin/log.c
+#, c-format
 msgid "insane in-reply-to: %s"
 msgstr "不正常的 in-reply-to:%s"
 
@@ -10664,6 +11024,14 @@
 msgstr "生成一封附函"
 
 #: builtin/log.c
+msgid "format-spec"
+msgstr "格式规格"
+
+#: builtin/log.c
+msgid "format spec used for the commit list in the cover letter"
+msgstr "附函中提交列表所用的格式规格"
+
+#: builtin/log.c
 msgid "use simple number sequence for output file names"
 msgstr "使用简单的数字序列作为输出文件名"
 
@@ -11779,11 +12147,21 @@
 
 #: builtin/multi-pack-index.c
 msgid ""
-"git multi-pack-index [<options>] write [--preferred-pack=<pack>][--refs-"
-"snapshot=<path>]"
+"git multi-pack-index [<options>] write [--preferred-pack=<pack>]\n"
+"  [--[no-]bitmap] [--[no-]incremental] [--[no-]stdin-packs]\n"
+"  [--refs-snapshot=<path>]"
 msgstr ""
-"git multi-pack-index [<选项>] write [--preferred-pack=<包>][--refs-snapshot=<"
-"路径>]"
+"git multi-pack-index [<选项>] write [--preferred-pack=<包>]\n"
+"  [--[no-]bitmap] [--[no-]incremental] [--[no-]stdin-packs]\n"
+"  [--refs-snapshot=<路径>]"
+
+#: builtin/multi-pack-index.c
+msgid ""
+"git multi-pack-index [<options>] compact [--[no-]incremental]\n"
+"  [--[no-]bitmap] <from> <to>"
+msgstr ""
+"git multi-pack-index [<选项>] compact [--[no-]incremental]\n"
+"  [--[no-]bitmap] <源> <目标>"
 
 #: builtin/multi-pack-index.c
 msgid "git multi-pack-index [<options>] verify"
@@ -11830,6 +12208,20 @@
 msgstr "用于选择位图提交的引用快照"
 
 #: builtin/multi-pack-index.c
+#, c-format
+msgid "could not find MIDX: %s"
+msgstr "找不到 MIDX:%s"
+
+#: builtin/multi-pack-index.c
+msgid "MIDX compaction endpoints must be unique"
+msgstr "MIDX 压缩的端点必须唯一"
+
+#: builtin/multi-pack-index.c
+#, c-format
+msgid "MIDX %s must be an ancestor of %s"
+msgstr "MIDX %s 必须是 %s 的祖先"
+
+#: builtin/multi-pack-index.c
 msgid ""
 "during repack, collect pack-files of smaller size into a batch that is "
 "larger than this size"
@@ -12580,13 +12972,13 @@
 
 #: builtin/pack-objects.c
 #, c-format
-msgid "packfile %s is a promisor but --exclude-promisor-objects was given"
-msgstr "包文件 %s 是承诺者,但已给出 --exclude-promisor-objects"
+msgid "could not find pack '%s'"
+msgstr "无法找到包 '%s'"
 
 #: builtin/pack-objects.c
 #, c-format
-msgid "could not find pack '%s'"
-msgstr "无法找到包 '%s'"
+msgid "packfile %s is a promisor but --exclude-promisor-objects was given"
+msgstr "包文件 %s 是承诺者,但已给出 --exclude-promisor-objects"
 
 #: builtin/pack-objects.c
 #, c-format
@@ -12623,10 +13015,6 @@
 "预期对象 ID,却得到垃圾数据:\n"
 " %s"
 
-#: builtin/pack-objects.c reachable.c
-msgid "could not load cruft pack .mtimes"
-msgstr "无法载入废弃包 .mtimes"
-
 #: builtin/pack-objects.c
 msgid "cannot open pack index"
 msgstr "无法打开包文件索引"
@@ -15331,59 +15719,32 @@
 
 #: builtin/replay.c
 #, c-format
-msgid "'%s' is not a valid commit-ish for %s"
-msgstr "'%s' 不是 %s 的有效提交号"
-
-#: builtin/replay.c
-msgid "need some commits to replay"
-msgstr "需要一些提交来重放"
-
-#: builtin/replay.c
-msgid "all positive revisions given must be references"
-msgstr "提供的所有正向版本必须为引用"
-
-#: builtin/replay.c
-msgid "argument to --advance must be a reference"
-msgstr "--advance 的参数必须是引用"
-
-#: builtin/replay.c
-msgid ""
-"cannot advance target with multiple sources because ordering would be ill-"
-"defined"
-msgstr "无法使用多个源推进目标,因为无法明确如何排序"
-
-#: builtin/replay.c
-#, c-format
 msgid "invalid %s value: '%s'"
 msgstr "无效的 %s 值:'%s'"
 
 #: builtin/replay.c
-msgid ""
-"(EXPERIMENTAL!) git replay ([--contained] --onto <newbase> | --advance "
-"<branch>) [--ref-action[=<mode>]] <revision-range>"
-msgstr ""
-"(试验中!)git replay ([--contained] --onto <新基线> | --advance <分支>) [--"
-"ref-action[=<模式>]] <版本范围>"
-
-#: builtin/replay.c
-msgid "make replay advance given branch"
-msgstr "重放时演进给定的分支"
-
-#: builtin/replay.c
-msgid "replay onto given commit"
-msgstr "重放到给定提交"
-
-#: builtin/replay.c
 msgid "update all branches that point at commits in <revision-range>"
 msgstr "更新指向 <版本范围> 中提交的所有分支"
 
 #: builtin/replay.c
+msgid "replay onto given commit"
+msgstr "重放到给定提交"
+
+#: builtin/replay.c
+msgid "make replay advance given branch"
+msgstr "在给定分支上重放并演进该分支"
+
+#: builtin/replay.c
+msgid "revert commits onto given branch"
+msgstr "在给定分支上还原提交"
+
+#: builtin/replay.c
 msgid "control ref update behavior (update|print)"
 msgstr "控制引用更新行为(update|print)"
 
 #: builtin/replay.c
-msgid "option --onto or --advance is mandatory"
-msgstr "选项 --onto 或 --advance 必须指定其一"
+msgid "exactly one of --onto, --advance, or --revert is required"
+msgstr "必须指定 --onto、--advance 或 --revert 之一"
 
 #: builtin/replay.c
 #, c-format
@@ -15393,39 +15754,16 @@
 msgstr ""
 "一些版本遍历选项将被覆盖,如 'struct rev_info' 中的 '%s' 位将被强制设定"
 
-#: builtin/replay.c
-#, c-format
-msgid "failed to begin ref transaction: %s"
-msgstr "无法开始引用事务:%s"
-
-#: builtin/replay.c
-msgid "error preparing revisions"
-msgstr "准备版本时错误"
-
-#: builtin/replay.c
-msgid "replaying down from root commit is not supported yet!"
-msgstr "目前还不支持从根提交向下重放!"
-
-#: builtin/replay.c
-msgid "replaying merge commits is not supported yet!"
-msgstr "目前还不支持重放合并提交!"
-
-#: builtin/replay.c
-#, c-format
-msgid "failed to update ref '%s': %s"
-msgstr "无法更新引用 '%s':%s"
-
-#: builtin/replay.c
-#, c-format
-msgid "failed to commit ref transaction: %s"
-msgstr "无法提交引用事务:%s"
-
 #: builtin/repo.c
 #, c-format
 msgid "key '%s' not found"
 msgstr "未找到键 '%s'"
 
 #: builtin/repo.c
+msgid "--keys can only be used with --format=lines or --format=nul"
+msgstr "--keys 只能与 --format=lines 或 --format=nul 一起使用"
+
+#: builtin/repo.c
 #, c-format
 msgid "invalid format '%s'"
 msgstr "无效格式 '%s'"
@@ -15443,6 +15781,14 @@
 msgstr "打印所有键/值"
 
 #: builtin/repo.c
+msgid "show keys"
+msgstr "显示键"
+
+#: builtin/repo.c
+msgid "--keys cannot be used with a <key> or --all"
+msgstr "--keys 不能与 <键> 或 --all 一起使用"
+
+#: builtin/repo.c
 msgid "unsupported output format"
 msgstr "不支持的输出格式"
 
@@ -15499,6 +15845,22 @@
 msgstr "磁盘大小"
 
 #: builtin/repo.c
+msgid "Largest objects"
+msgstr "最大的对象"
+
+#: builtin/repo.c
+msgid "Maximum size"
+msgstr "最大大小"
+
+#: builtin/repo.c
+msgid "Maximum parents"
+msgstr "最大父提交数"
+
+#: builtin/repo.c
+msgid "Maximum entries"
+msgstr "最大条目数"
+
+#: builtin/repo.c
 msgid "Repository structure"
 msgstr "仓库结构"
 
@@ -16213,6 +16575,55 @@
 msgid "Unknown hash algorithm"
 msgstr "未知的哈希算法"
 
+#: builtin/show-index.c
+msgid "assuming SHA-1; use --object-format to override"
+msgstr "假定使用 SHA-1;使用 --object-format 可覆盖"
+
+#: builtin/show-index.c
+msgid "unable to read header"
+msgstr "无法读取头信息"
+
+#: builtin/show-index.c
+msgid "unknown index version"
+msgstr "未知的索引版本"
+
+#: builtin/show-index.c
+msgid "unable to read index"
+msgstr "无法读取索引"
+
+#: builtin/show-index.c
+msgid "corrupt index file"
+msgstr "索引文件损坏"
+
+#: builtin/show-index.c
+#, c-format
+msgid "unable to read entry %u/%u"
+msgstr "无法读取条目 %u/%u"
+
+#: builtin/show-index.c
+#, c-format
+msgid "unable to read sha1 %u/%u"
+msgstr "无法读取 sha1 %u/%u"
+
+#: builtin/show-index.c
+#, c-format
+msgid "unable to read crc %u/%u"
+msgstr "无法读取 crc %u/%u"
+
+#: builtin/show-index.c
+#, c-format
+msgid "unable to read 32b offset %u/%u"
+msgstr "无法读取 32 位偏移 %u/%u"
+
+#: builtin/show-index.c
+msgid "inconsistent 64b offset index"
+msgstr "64 位偏移索引不一致"
+
+#: builtin/show-index.c
+#, c-format
+msgid "unable to read 64b offset %u"
+msgstr "无法读取 64 位偏移 %u"
+
 #: builtin/show-ref.c
 msgid ""
 "git show-ref [--head] [-d | --dereference]\n"
@@ -16508,21 +16919,6 @@
 
 #: builtin/stash.c
 msgid ""
-"git stash [push [-p | --patch] [-S | --staged] [-k | --[no-]keep-index] [-q "
-"| --quiet]\n"
-"          [-u | --include-untracked] [-a | --all] [(-m | --message) "
-"<message>]\n"
-"          [--pathspec-from-file=<file> [--pathspec-file-nul]]\n"
-"          [--] [<pathspec>...]]"
-msgstr ""
-"git stash [push [-p | --patch] [-S | --staged] [-k | --[no-]keep-index] [-q "
-"| --quiet]\n"
-"          [-u | --include-untracked] [-a | --all] [(-m | --message) <消息>]\n"
-"          [--pathspec-from-file=<文件> [--pathspec-file-nul]]\n"
-"          [--] [<路径规格>...]]"
-
-#: builtin/stash.c
-msgid ""
 "git stash save [-p | --patch] [-S | --staged] [-k | --[no-]keep-index] [-q | "
 "--quiet]\n"
 "          [-u | --include-untracked] [-a | --all] [<message>]"
@@ -16853,6 +17249,10 @@
 msgstr "无法获得子模组 '%s' 的仓库句柄"
 
 #: builtin/submodule--helper.c
+msgid "git submodule--helper get-default-remote <path>"
+msgstr "git submodule--helper get-default-remote <路径>"
+
+#: builtin/submodule--helper.c
 #, c-format
 msgid "No url found for submodule path '%s' in .gitmodules"
 msgstr "在 .gitmodules 中未找到子模组路径 '%s' 的 url"
@@ -16895,6 +17295,16 @@
 
 #: builtin/submodule--helper.c
 #, c-format
+msgid ""
+"failed to set a valid default config for 'submodule.%s.gitdir'. Please "
+"ensure it is set, for example by running something like: 'git config "
+"submodule.%s.gitdir .git/modules/%s'"
+msgstr ""
+"无法为 'submodule.%s.gitdir' 设置有效的默认配置。请确保已设置,例如运行类"
+"似:'git config submodule.%s.gitdir .git/modules/%s'"
+
+#: builtin/submodule--helper.c
+#, c-format
 msgid "Failed to register url for submodule path '%s'"
 msgstr "无法为子模组路径 '%s' 注册 url"
 
@@ -17000,6 +17410,30 @@
 msgstr "无法为 HEAD 获取一个版本"
 
 #: builtin/submodule--helper.c
+msgid "git submodule--helper gitdir <name>"
+msgstr "git submodule--helper gitdir <名称>"
+
+#: builtin/submodule--helper.c
+msgid ""
+"could not set core.repositoryformatversion to 1.\n"
+"Please set it for migration to work, for example:\n"
+"git config core.repositoryformatversion 1"
+msgstr ""
+"无法将 core.repositoryformatversion 设为 1。\n"
+"请进行设置以便迁移,例如:\n"
+"git config core.repositoryformatversion 1"
+
+#: builtin/submodule--helper.c
+msgid ""
+"could not enable submodulePathConfig extension. It is required\n"
+"for migration to work. Please enable it in the root repo:\n"
+"git config extensions.submodulePathConfig true"
+msgstr ""
+"无法启用 submodulePathConfig 扩展。迁移需要此扩展。\n"
+"请在根仓库中启用:\n"
+"git config extensions.submodulePathConfig true"
+
+#: builtin/submodule--helper.c
 #, c-format
 msgid "Synchronizing submodule url for '%s'\n"
 msgstr "为 '%s' 同步子模组 url\n"
@@ -17108,11 +17542,6 @@
 msgid "Value '%s' for submodule.alternateLocation is not recognized"
 msgstr "无法识别 submodule.alternateLocation 的取值 '%s'"
 
-#: builtin/submodule--helper.c submodule.c
-#, c-format
-msgid "refusing to create/use '%s' in another submodule's git dir"
-msgstr "拒绝在另一个子模组的 git 目录中创建/使用 '%s'"
-
 #: builtin/submodule--helper.c
 #, c-format
 msgid "directory not empty: '%s'"
@@ -17123,6 +17552,15 @@
 msgid "clone of '%s' into submodule path '%s' failed"
 msgstr "无法克隆 '%s' 到子模组路径 '%s'"
 
+#: builtin/submodule--helper.c submodule.c
+#, c-format
+msgid ""
+"refusing to create/use '%s' in another submodule's git dir. Enabling "
+"extensions.submodulePathConfig should fix this."
+msgstr ""
+"拒绝在另一子模组的 git 目录中创建/使用 '%s'。启用 "
+"extensions.submodulePathConfig 应可解决此问题。"
+
 #: builtin/submodule--helper.c
 #, c-format
 msgid "could not get submodule directory for '%s'"
@@ -17607,7 +18045,7 @@
 #: builtin/tag.c
 #, c-format
 msgid "Deleted tag '%s' (was %s)\n"
-msgstr "已删除标签 '%s'(曾为 %s)\n"
+msgstr "已删除标签 '%s'(原为 %s)\n"
 
 #: builtin/tag.c
 #, c-format
@@ -17769,7 +18207,7 @@
 #: builtin/tag.c
 #, c-format
 msgid "Updated tag '%s' (was %s)\n"
-msgstr "已更新标签 '%s'(曾为 %s)\n"
+msgstr "已更新标签 '%s'(原为 %s)\n"
 
 #: builtin/unpack-objects.c
 msgid "pack exceeds maximum allowed size"
@@ -18222,8 +18660,8 @@
 msgstr "报告清除的工作区"
 
 #: builtin/worktree.c
-msgid "expire working trees older than <time>"
-msgstr "将早于 <时间> 的工作区过期"
+msgid "prune missing working trees older than <time>"
+msgstr "清除早于 <时间> 的缺失工作区"
 
 #: builtin/worktree.c
 #, c-format
@@ -18303,15 +18741,8 @@
 msgstr "准备工作区(分离头指针 %s)"
 
 #: builtin/worktree.c
-#, c-format
-msgid ""
-"HEAD points to an invalid (or orphaned) reference.\n"
-"HEAD path: '%s'\n"
-"HEAD contents: '%s'"
-msgstr ""
-"HEAD 指向了一个无效的(或孤立的)引用。\n"
-"HEAD 路径: '%s'\n"
-"HEAD 内容: '%s'"
+msgid "HEAD points to an invalid (or orphaned) reference.\n"
+msgstr "HEAD 指向了一个无效的(或孤立的)引用。\n"
 
 #: builtin/worktree.c
 msgid ""
@@ -18384,8 +18815,8 @@
 msgstr "显示扩展的注释和原因(如果有)"
 
 #: builtin/worktree.c
-msgid "add 'prunable' annotation to worktrees older than <time>"
-msgstr "向早于 <时间> 的工作区添加“可修剪”注释"
+msgid "add 'prunable' annotation to missing worktrees older than <time>"
+msgstr "为早于 <时间> 的缺失工作区添加 'prunable' 标注"
 
 #: builtin/worktree.c
 msgid "terminate records with a NUL character"
@@ -19005,6 +19436,10 @@
 msgstr "显示 Git 的帮助信息"
 
 #: command-list.h
+msgid "EXPERIMENTAL: Rewrite history"
+msgstr "实验性:重写历史"
+
+#: command-list.h
 msgid "Run git hooks"
 msgstr "运行 git 钩子"
 
@@ -19142,8 +19577,8 @@
 msgstr "打包分支和标签以实现高效的仓库访问"
 
 #: command-list.h
-msgid "Compute unique ID for a patch"
-msgstr "计算一个补丁的唯一 ID"
+msgid "Compute unique IDs for patches"
+msgstr "计算补丁的唯一 ID"
 
 #: command-list.h
 msgid "Prune all unreachable objects from the object database"
@@ -19835,6 +20270,11 @@
 msgid "could not parse commit %s"
 msgstr "无法解析提交 %s"
 
+#: commit.c object.c
+#, c-format
+msgid "object %s is a %s, not a %s"
+msgstr "对象 %s 是一个 %s,不是一个 %s"
+
 #: commit.c
 #, c-format
 msgid "%s %s is not a commit!"
@@ -20328,11 +20768,6 @@
 
 #: config.c
 #, c-format
-msgid "bad boolean config value '%s' for '%s'"
-msgstr "配置变量 '%2$s' 的布尔值 '%1$s' 无效"
-
-#: config.c
-#, c-format
 msgid "failed to expand user dir in: '%s'"
 msgstr "无法扩展用户目录:'%s'"
 
@@ -21138,6 +21573,10 @@
 msgstr "ws-error-highlight=%.*s 之后未知的值"
 
 #: diff.c
+msgid "--find-object requires a git repository"
+msgstr "--find-object 需要 git 仓库"
+
+#: diff.c
 #, c-format
 msgid "unable to resolve '%s'"
 msgstr "无法解析 '%s'"
@@ -21613,10 +22052,6 @@
 msgstr "<深度>"
 
 #: diff.c
-msgid "maximum tree depth to recurse"
-msgstr "递归的最大树深度"
-
-#: diff.c
 msgid "<file>"
 msgstr "<文件>"
 
@@ -22044,6 +22479,11 @@
 msgstr "git fetch-pack:预期响应结束包"
 
 #: fetch-pack.c
+#, c-format
+msgid "couldn't resolve 'auto' filter '%s': %s"
+msgstr "无法解析 'auto' 过滤器 '%s':%s"
+
+#: fetch-pack.c
 msgid "no matching remote head"
 msgstr "没有匹配的远程头引用"
 
@@ -22539,6 +22979,18 @@
 "因为没有将钩子 '%s' 设置为可执行,钩子被忽略。您可以通过\n"
 "配置 `git config set advice.ignoredHook false` 来关闭这条警告。"
 
+#: hook.c
+#, c-format
+msgid "disabled hook '%s' has no command configured"
+msgstr "已禁用的钩子 '%s' 未配置命令"
+
+#: hook.c
+#, c-format
+msgid ""
+"'hook.%s.command' must be configured or 'hook.%s.event' must be removed; "
+"aborting."
+msgstr "必须配置 'hook.%s.command',或移除 'hook.%s.event';正在中止。"
+
 #: http-fetch.c
 msgid "not a git repository"
 msgstr "不是 Git 仓库"
@@ -22583,7 +23035,7 @@
 
 #: http.c
 msgid "refusing to read cookies from http.cookiefile '-'"
-msgstr "拒绝从 http.cookiefile '-' 读取 Cookie"
+msgstr "拒绝从 http.cookiefile '-' 读取 cookie"
 
 #: http.c
 msgid "ignoring http.savecookies for empty http.cookiefile"
@@ -22600,6 +23052,23 @@
 "     请求:%s\n"
 "   重定向:%s"
 
+#: http.c
+#, c-format
+msgid ""
+"response requested a delay greater than http.maxRetryTime (%ld > %ld seconds)"
+msgstr "响应请求的延迟超过 http.maxRetryTime(%ld > %ld 秒)"
+
+#: http.c
+#, c-format
+msgid ""
+"configured http.retryAfter exceeds http.maxRetryTime (%ld > %ld seconds)"
+msgstr "配置的 http.retryAfter 超过了 http.maxRetryTime(%ld > %ld 秒)"
+
+#: http.c
+#, c-format
+msgid "rate limited, waiting %ld seconds before retry"
+msgstr "速率受限,等待 %ld 秒后重试"
+
 #: http.h
 #, c-format
 msgid ""
@@ -22698,6 +23167,10 @@
 "(例如:'git config imap.folder Drafts')"
 
 #: list-objects-filter-options.c
+msgid "'auto' filter not supported by this command"
+msgstr "此命令不支持 'auto' 过滤器"
+
+#: list-objects-filter-options.c
 msgid "expected 'tree:<depth>'"
 msgstr "期望 'tree:<深度>'"
 
@@ -22721,6 +23194,10 @@
 msgstr "必须对 sub-filter-spec 中的字符进行转义:'%c'"
 
 #: list-objects-filter-options.c
+msgid "an 'auto' filter cannot be combined"
+msgstr "'auto' 过滤器不能与其他过滤器组合"
+
+#: list-objects-filter-options.c
 msgid "expected something after combine:"
 msgstr "期望在组合后有一些东西:"
 
@@ -22729,6 +23206,10 @@
 msgstr "不能混用多种过滤规格"
 
 #: list-objects-filter-options.c
+msgid "an 'auto' filter is incompatible with any other filter"
+msgstr "'auto' 过滤器与任何其他过滤器不兼容"
+
+#: list-objects-filter-options.c
 msgid "unable to upgrade repository format to support partial clone"
 msgstr "无法升级仓库格式以支持部分克隆"
 
@@ -22767,21 +23248,44 @@
 
 #: lockfile.c
 #, c-format
+msgid "could not write lock pid file '%s'"
+msgstr "无法写入锁 PID 文件 '%s'"
+
+#: lockfile.c
+#, c-format
+msgid "malformed lock pid file '%s'"
+msgstr "锁 PID 文件 '%s' 格式错误"
+
+#: lockfile.c
+#, c-format
 msgid ""
-"Unable to create '%s.lock': %s.\n"
+"Unable to create '%s': %s.\n"
 "\n"
-"Another git process seems to be running in this repository, e.g.\n"
-"an editor opened by 'git commit'. Please make sure all processes\n"
-"are terminated then try again. If it still fails, a git process\n"
-"may have crashed in this repository earlier:\n"
-"remove the file manually to continue."
 msgstr ""
-"无法创建 '%s.lock':%s。\n"
+"无法创建 '%s':%s。\n"
 "\n"
-"似乎另一个 Git 进程在这个仓库中运行,例如:'git commit' 命令打\n"
-"开了一个编辑器。请确认所有进程都已经关闭然后重试。如果仍然报错,\n"
-"可能之前有一个 Git 进程在这个仓库中异常退出:\n"
-"手动删除这个文件再继续。"
+
+#: lockfile.c
+#, c-format
+msgid ""
+"Lock may be held by process %<PRIuMAX>; if no git process is running, the "
+"lock file may be stale (PIDs can be reused)"
+msgstr ""
+"锁可能仍由进程 %<PRIuMAX> 持有;若无 git 进程在运行,锁文件可能已过期(PID 可"
+"能被复用)"
+
+#: lockfile.c
+#, c-format
+msgid ""
+"Lock was held by process %<PRIuMAX>, which is no longer running; the lock "
+"file appears to be stale"
+msgstr "锁曾由进程 %<PRIuMAX> 持有,但该进程已不再运行;锁文件看起来已过期"
+
+#: lockfile.c
+msgid ""
+"Another git process seems to be running in this repository, or the lock file "
+"may be stale"
+msgstr "另一 git 进程似乎正在此仓库中运行,或者锁文件可能已过期"
 
 #: lockfile.c
 #, c-format
@@ -22944,8 +23448,8 @@
 "Path updated: %s renamed to %s in %s, inside a directory that was renamed in "
 "%s; moving it to %s."
 msgstr ""
-"路径已更新:%1$s 重命名为 %3$s 中的 %2$s,而该目录被重命名到 %4$s 中,将其移"
-"动到 %5$s。"
+"路径已更新:%1$s 在 %3$s 中被重命名为 %2$s,而其所在目录又在 %4$s 中被重命"
+"名,因此将其移动到 %5$s。"
 
 #: merge-ort.c
 #, c-format
@@ -23129,8 +23633,17 @@
 msgstr "格式错误的行:%s"
 
 #: midx-write.c
-msgid "could not load pack"
-msgstr "无法载入包"
+#, c-format
+msgid "could not load pack %d"
+msgstr "无法载入包 %d"
+
+#: midx-write.c
+msgid "too many packs, unable to compact"
+msgstr "包过多,无法压缩"
+
+#: midx-write.c pack-revindex.c
+msgid "could not determine preferred pack"
+msgstr "无法确定首选包"
 
 #: midx-write.c
 #, c-format
@@ -23143,6 +23656,15 @@
 msgstr "无法清理位于 %s 的多包索引"
 
 #: midx-write.c
+#, c-format
+msgid "unknown MIDX version: %d"
+msgstr "未知的 MIDX 版本:%d"
+
+#: midx-write.c
+msgid "cannot perform MIDX compaction with v1 format"
+msgstr "无法在 v1 格式下执行 MIDX 压缩"
+
+#: midx-write.c
 msgid "ignoring existing multi-pack-index; checksum mismatch"
 msgstr "忽略已存在的多包索引,校验码不匹配"
 
@@ -23846,11 +24368,6 @@
 
 #: object.c
 #, c-format
-msgid "object %s is a %s, not a %s"
-msgstr "对象 %s 是一个 %s,不是一个 %s"
-
-#: object.c
-#, c-format
 msgid "object %s has unknown type id %d"
 msgstr "对象 %s 有未知的类型 id %d"
 
@@ -23861,6 +24378,11 @@
 
 #: object.c
 #, c-format
+msgid "unable to open object stream for %s"
+msgstr "无法打开 %s 的对象流"
+
+#: object.c
+#, c-format
 msgid "hash mismatch %s"
 msgstr "哈希值与 %s 不匹配"
 
@@ -23880,18 +24402,6 @@
 msgstr "%s:忽略备用对象库,嵌套太深"
 
 #: odb.c
-msgid "unable to fdopen alternates lockfile"
-msgstr "无法 fdopen alternates 的锁文件"
-
-#: odb.c
-msgid "unable to read alternates file"
-msgstr "无法读取 alternates 文件"
-
-#: odb.c
-msgid "unable to move new alternates file into place"
-msgstr "无法将新的 alternates 文件移动到位"
-
-#: odb.c
 #, c-format
 msgid "path '%s' does not exist"
 msgstr "路径 '%s' 不存在"
@@ -23946,6 +24456,18 @@
 msgid "%s is not a valid '%s' object"
 msgstr "%s 不是有效的 '%s' 对象"
 
+#: odb/source-files.c
+msgid "unable to fdopen alternates lockfile"
+msgstr "无法 fdopen alternates 的锁文件"
+
+#: odb/source-files.c
+msgid "unable to read alternates file"
+msgstr "无法读取 alternates 文件"
+
+#: odb/source-files.c
+msgid "unable to move new alternates file into place"
+msgstr "无法将新的 alternates 文件移动到位"
+
 #: pack-bitmap-write.c
 #, c-format
 msgid "duplicate entry when writing bitmap index: %s"
@@ -24205,10 +24727,6 @@
 msgid "multi-pack-index reverse-index chunk is the wrong size"
 msgstr "多包索引的反向索引块大小错误"
 
-#: pack-revindex.c
-msgid "could not determine preferred pack"
-msgstr "无法确定首选包"
-
 #: pack-write.c
 msgid "cannot both write and verify reverse index"
 msgstr "不能同时写入和校验反向索引"
@@ -24239,6 +24757,11 @@
 
 #: packfile.c
 #, c-format
+msgid "could not load .mtimes for cruft pack '%s'"
+msgstr "无法为废弃包 '%s' 载入 .mtimes"
+
+#: packfile.c
+#, c-format
 msgid "offset before start of pack index for %s (corrupt index?)"
 msgstr "偏移量在 %s 的包索引开始之前(损坏的索引?)"
 
@@ -24657,6 +25180,11 @@
 msgid "unable to parse --pretty format"
 msgstr "无法解析 --pretty 格式"
 
+#: pretty.c
+#, c-format
+msgid "%s is not supported by this command"
+msgstr "此命令不支持 %s"
+
 #: promisor-remote.c
 msgid "lazy fetching disabled; some objects may not be available"
 msgstr "已禁用延迟获取,某些对象可能不可用"
@@ -24710,6 +25238,25 @@
 
 #: promisor-remote.c
 #, c-format
+msgid ""
+"Storing new %s from server for remote '%s'.\n"
+"    '%s' -> '%s'\n"
+msgstr ""
+"正在为远程 '%2$s' 存储来自服务器新的 %1$s。\n"
+"    '%3$s' -> '%4$s'\n"
+
+#: promisor-remote.c
+#, c-format
+msgid "invalid filter '%s' for remote '%s' will not be stored: %s"
+msgstr "远程 '%2$s' 的无效过滤器 '%1$s' 将不会保存:%3$s"
+
+#: promisor-remote.c
+#, c-format
+msgid "invalid token '%s' for remote '%s' will not be stored"
+msgstr "远程 '%2$s' 的无效令牌 '%1$s' 将不会保存"
+
+#: promisor-remote.c
+#, c-format
 msgid "unknown '%s' value for '%s' config option"
 msgstr "配置项 '%2$s' 为未知的取值 '%1$s'"
 
@@ -24718,6 +25265,11 @@
 msgid "accepted promisor remote '%s' not found"
 msgstr "未找到已接受的承诺者远程 '%s'"
 
+#: promisor-remote.c
+#, c-format
+msgid "promisor remote '%s' advertised invalid filter '%s': %s"
+msgstr "promisor 远程 '%s'通告了无效的过滤器 '%s':%s"
+
 #: protocol-caps.c
 msgid "object-info: expected flush after arguments"
 msgstr "object-info:在参数之后应有一个 flush 包"
@@ -24857,7 +25409,7 @@
 #: read-cache.c
 #, c-format
 msgid "%s: can only add regular files, symbolic links or git-directories"
-msgstr "%s:只能添加常规文件、符号链接或 Git 目录"
+msgstr "%s:只能添加普通文件、符号链接或 Git 目录"
 
 #: read-cache.c
 #, c-format
@@ -25024,6 +25576,15 @@
 
 #: read-cache.c
 #, c-format
+msgid ""
+"Skipping submodule due to ignore=all: %s\n"
+"Use --force if you really want to add the submodule."
+msgstr ""
+"由于 ignore=all 而跳过子模组:%s\n"
+"若确实要添加该子模组,请使用 --force。"
+
+#: read-cache.c
+#, c-format
 msgid "unexpected diff status %c"
 msgstr "意外的差异状态 %c"
 
@@ -25457,6 +26018,11 @@
 msgstr "没有 '%s' 的引用日志"
 
 #: refs.c
+#, c-format
+msgid "in '%s' phase, update aborted by the reference-transaction hook"
+msgstr "在 '%s' 阶段,reference-transaction 钩子中止了更新"
+
+#: refs.c
 msgid "Checking references consistency"
 msgstr "正在检查引用一致性"
 
@@ -25585,10 +26151,6 @@
 msgstr "在隔离环境中禁止更新引用"
 
 #: refs.c
-msgid "ref updates aborted by hook"
-msgstr "引用更新被钩子中止"
-
-#: refs.c
 #, c-format
 msgid "'%s' exists; cannot create '%s'"
 msgstr "'%s' 已存在,无法创建 '%s'"
@@ -25741,7 +26303,7 @@
 #: refs/reftable-backend.c
 #, c-format
 msgid "unable to compact stack: %s"
-msgstr "无法压缩堆栈:%s"
+msgstr "无法压缩栈:%s"
 
 #: refs/reftable-backend.c
 #, c-format
@@ -25756,7 +26318,7 @@
 #: refs/reftable-backend.c
 #, c-format
 msgid "reftable stack for worktree '%s' is broken"
-msgstr "工作区 '%s' 的 reftable 堆栈已损坏"
+msgstr "工作区 '%s' 的 reftable 栈已损坏"
 
 #: refs/reftable-backend.c
 #, c-format
@@ -25819,6 +26381,16 @@
 
 #: remote-curl.c
 #, c-format
+msgid "rate limited by '%s', please try again in %ld seconds"
+msgstr "被 '%s' 限制速率,请在 %ld 秒后重试"
+
+#: remote-curl.c
+#, c-format
+msgid "rate limited by '%s', please try again later"
+msgstr "被 '%s' 限制速率,请稍后再试"
+
+#: remote-curl.c
+#, c-format
 msgid "unable to access '%s': %s"
 msgstr "无法访问 '%s':%s"
 
@@ -25883,7 +26455,7 @@
 
 #: remote-curl.c
 msgid "cannot fetch by sha1 over smart http"
-msgstr "无法通过智能 HTTP 获取 SHA-1"
+msgstr "无法经由智能 HTTP 通过 SHA-1 获取"
 
 #: remote-curl.c
 #, c-format
@@ -26146,12 +26718,10 @@
 
 #: remote.c
 #, c-format
-msgid "Your branch is based on '%s', but the upstream is gone.\n"
-msgstr "您的分支基于 '%s',但此上游分支已经不存在。\n"
-
-#: remote.c
-msgid "  (use \"git branch --unset-upstream\" to fixup)\n"
-msgstr "  (使用 \"git branch --unset-upstream\" 来修复)\n"
+msgid ""
+"ignoring value '%s' for status.compareBranches, only @{upstream} and @{push} "
+"are supported"
+msgstr "忽略 status.compareBranches 的值 '%s',仅支持 @{upstream} 和 @{push}"
 
 #: remote.c
 #, c-format
@@ -26215,6 +26785,15 @@
 
 #: remote.c
 #, c-format
+msgid "Your branch is based on '%s', but the upstream is gone.\n"
+msgstr "您的分支基于 '%s',但此上游分支已经不存在。\n"
+
+#: remote.c
+msgid "  (use \"git branch --unset-upstream\" to fixup)\n"
+msgstr "  (使用 \"git branch --unset-upstream\" 来修复)\n"
+
+#: remote.c
+#, c-format
 msgid "cannot parse expected object name '%s'"
 msgstr "无法解析期望的对象名 '%s'"
 
@@ -26307,6 +26886,40 @@
 msgid "replace depth too high for object %s"
 msgstr "对象 %s 的替换层级太深"
 
+#: replay.c
+#, c-format
+msgid "'%s' is not a valid commit-ish for %s"
+msgstr "'%s' 不是 %s 的有效提交号"
+
+#: replay.c
+#, c-format
+msgid "argument to %s must be a reference"
+msgstr "%s 的参数必须是引用"
+
+#: replay.c
+#, c-format
+msgid ""
+"'%s' cannot be used with multiple revision ranges because the ordering would "
+"be ill-defined"
+msgstr "不能与多个版本范围一起使用 '%s',否则顺序将无法明确界定"
+
+#: replay.c
+msgid "need some commits to replay"
+msgstr "需要一些提交来重放"
+
+#: replay.c
+msgid "all positive revisions given must be references"
+msgstr "提供的所有正向版本必须为引用"
+
+#: replay.c sequencer.c
+#, c-format
+msgid "'%s' is not a valid refname"
+msgstr "'%s' 不是一个有效的引用名"
+
+#: repository.c
+msgid "compatibility hash algorithm support requires Rust"
+msgstr "兼容性哈希算法的支持需要 Rust"
+
 #: rerere.c
 msgid "corrupt MERGE_RR"
 msgstr "损坏的 MERGE_RR"
@@ -27193,11 +27806,6 @@
 
 #: sequencer.c
 #, c-format
-msgid "'%s' is not a valid refname"
-msgstr "'%s' 不是一个有效的引用名"
-
-#: sequencer.c
-#, c-format
 msgid "update-ref requires a fully qualified refname e.g. refs/heads/%s"
 msgstr "update-ref 需要一个完整的引用名,例如:refs/heads/%s"
 
@@ -27303,6 +27911,18 @@
 msgstr "无法在拣选中执行回退。"
 
 #: sequencer.c
+msgid "trailers file contains empty line"
+msgstr "尾注文件包含空行"
+
+#: sequencer.c
+msgid "trailers file is empty"
+msgstr "尾注文件为空"
+
+#: sequencer.c
+msgid "cannot read trailers files"
+msgstr "无法读取尾注文件"
+
+#: sequencer.c
 msgid "unusable squash-onto"
 msgstr "不可用的 squash-onto"
 
@@ -27887,6 +28507,16 @@
 "\tgit config --global --add safe.directory %s"
 
 #: setup.c
+#, c-format
+msgid "error reading '%s'"
+msgstr "读取 '%s' 时出错"
+
+#: setup.c
+#, c-format
+msgid "not a regular file: '%s'"
+msgstr "不是普通文件:'%s'"
+
+#: setup.c
 msgid "Unable to read current working directory"
 msgstr "无法读取当前工作目录"
 
@@ -27916,6 +28546,11 @@
 
 #: setup.c
 #, c-format
+msgid "unknown ref storage format: '%s'"
+msgstr "未知的引用存储格式:'%s'"
+
+#: setup.c
+#, c-format
 msgid ""
 "problem with core.sharedRepository filemode value (0%.3o).\n"
 "The owner of files must always have read and write permissions."
@@ -28322,11 +28957,6 @@
 
 #: submodule.c
 #, c-format
-msgid "refusing to move '%s' into an existing git dir"
-msgstr "拒绝移动 '%s' 到现存 Git 目录中"
-
-#: submodule.c
-#, c-format
 msgid ""
 "Migrating git directory of '%s%s' from\n"
 "'%s' to\n"
@@ -28345,6 +28975,29 @@
 msgid "ls-tree returned unexpected return code %d"
 msgstr "ls-tree 返回意外返回码 %d"
 
+#: submodule.c
+#, c-format
+msgid ""
+"the 'submodule.%s.gitdir' config does not exist for module '%s'. Please "
+"ensure it is set, for example by running something like: 'git config "
+"submodule.%s.gitdir .git/modules/%s'. For details see the "
+"extensions.submodulePathConfig documentation."
+msgstr ""
+"模块 '%2$s' 缺少 'submodule.%1$s.gitdir' 配置。请确保已设置,例如运行类"
+"似:'git config submodule.%3$s.gitdir .git/modules/%4$s'。详细信息请参见 "
+"extensions.submodulePathConfig 文档。"
+
+#: submodule.c
+msgid ""
+"enabling extensions.submodulePathConfig might fix the following error, if "
+"it's not already enabled."
+msgstr "若尚未启用,启用 extensions.submodulePathConfig 可能修复以下错误。"
+
+#: submodule.c
+#, c-format
+msgid "refusing to create/use '%s' in another submodule's  git dir."
+msgstr "拒绝在另一子模组的 git 目录中创建/使用 '%s'。"
+
 #: symlinks.c
 #, c-format
 msgid "failed to lstat '%s'"
@@ -28416,6 +29069,11 @@
 msgstr "太多提交标记为可达"
 
 #: t/helper/test-read-midx.c
+#, c-format
+msgid "could not find MIDX with checksum %s"
+msgstr "找不到校验和为 %s 的 MIDX"
+
+#: t/helper/test-read-midx.c
 msgid "could not determine MIDX preferred pack"
 msgstr "无法确定多包索引的首选包"
 
@@ -28534,6 +29192,34 @@
 msgid "empty trailer token in trailer '%.*s'"
 msgstr "尾注 '%.*s' 的键为空"
 
+#: trailer.c
+msgid "empty --trailer argument"
+msgstr "空的 --trailer 参数"
+
+#: trailer.c
+#, c-format
+msgid "invalid trailer '%s': missing key before separator"
+msgstr "无效的尾注 '%s':分隔符前缺少键"
+
+#: trailer.c wrapper.c
+#, c-format
+msgid "could not stat %s"
+msgstr "无法对 %s 调用 stat"
+
+#: trailer.c
+#, c-format
+msgid "file %s is not a regular file"
+msgstr "文件 %s 不是一个普通文件"
+
+#: trailer.c
+#, c-format
+msgid "file %s is not writable by user"
+msgstr "文件 %s 用户不可写"
+
+#: trailer.c
+msgid "could not write to temporary file"
+msgstr "无法写入临时文件"
+
 #: transport-helper.c
 msgid "full write to remote helper failed"
 msgstr "完整写入远程助手失败"
@@ -30167,8 +30853,13 @@
 msgstr "如下文件含 8bit 内容,但没有声明一个 Content-Transfer-Encoding。\n"
 
 #: git-send-email.perl
-msgid "Which 8bit encoding should I declare [UTF-8]? "
-msgstr "要声明哪种 8bit 编码格式 [UTF-8]?"
+msgid "Declare which 8bit encoding to use [default: UTF-8]? "
+msgstr "声明要使用的 8 位编码 [默认:UTF-8]? "
+
+#: git-send-email.perl
+#, perl-format
+msgid "'%s' does not appear to be a valid charset name. Use it anyway [y/N]? "
+msgstr "'%s' 似乎不是有效的字符集名称。仍要使用吗 [y/N]? "
 
 #: git-send-email.perl
 #, perl-format
@@ -30213,6 +30904,11 @@
 msgstr "CA 路径 \"%s\" 不存在"
 
 #: git-send-email.perl
+#, perl-format
+msgid "Only client key \"%s\" specified"
+msgstr "仅指定了客户端密钥 \"%s\""
+
+#: git-send-email.perl
 msgid ""
 "    The Cc list above has been expanded by additional\n"
 "    addresses found in the patch commit message. By default\n"
diff --git a/po/zh_TW.po b/po/zh_TW.po
index 7172d6d..fe31e18 100644
--- a/po/zh_TW.po
+++ b/po/zh_TW.po
@@ -30,8 +30,8 @@
 msgstr ""
 "Project-Id-Version: Git\n"
 "Report-Msgid-Bugs-To: Git Mailing List <git@vger.kernel.org>\n"
-"POT-Creation-Date: 2026-01-28 22:23+0800\n"
-"PO-Revision-Date: 2026-01-28 22:42+0800\n"
+"POT-Creation-Date: 2026-04-19 19:19+0800\n"
+"PO-Revision-Date: 2026-04-19 13:40+0000\n"
 "Last-Translator: Yi-Jyun Pan <pan93412@gmail.com>\n"
 "Language-Team: Chinese (Traditional Han script) <https://weblate.slat.org/"
 "projects/git-po/git-cli/zh_Hant/>\n"
@@ -40,21 +40,18 @@
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
 "Plural-Forms: nplurals=1; plural=0;\n"
-"X-Generator: Poedit 3.8\n"
+"X-Generator: Weblate 5.16.2\n"
 "X-ZhConverter: 繁化姬 dict-f4bc617e-r910 @ 2019/11/16 20:23:12 | https://"
 "zhconvert.org\n"
 
 #: add-interactive.c
 #, c-format
-msgid "%s cannot be negative"
-msgstr "%s 不能是負數"
-
-#: add-interactive.c
-#, c-format
 msgid "Huh (%s)?"
 msgstr "嗯(%s)?"
 
-#: add-interactive.c builtin/merge.c builtin/rebase.c reset.c sequencer.c
+#: add-interactive.c add-interactive.c builtin/merge.c
+#: builtin/rebase.c builtin/rebase.c reset.c sequencer.c
+#: sequencer.c sequencer.c
 msgid "could not read index"
 msgstr "無法讀取索引"
 
@@ -74,12 +71,14 @@
 msgid "Update"
 msgstr "更新"
 
-#: add-interactive.c
+#: add-interactive.c add-interactive.c add-interactive.c
 #, c-format
 msgid "could not stage '%s'"
 msgstr "無法暫存「%s」"
 
-#: add-interactive.c builtin/stash.c reset.c sequencer.c
+#: add-interactive.c add-interactive.c builtin/stash.c
+#: builtin/stash.c builtin/stash.c builtin/stash.c
+#: builtin/stash.c reset.c sequencer.c
 msgid "could not write index"
 msgstr "無法寫入索引"
 
@@ -94,7 +93,8 @@
 msgid "note: %s is untracked now.\n"
 msgstr "註:現已不再追蹤 %s。\n"
 
-#: add-interactive.c apply.c builtin/checkout.c builtin/reset.c
+#: add-interactive.c apply.c builtin/checkout.c
+#: builtin/reset.c
 #, c-format
 msgid "make_cache_entry failed for path '%s'"
 msgstr "對「%s」路徑執行 make_cache_entry 失敗"
@@ -175,7 +175,7 @@
 msgid "add contents of untracked files to the staged set of changes"
 msgstr "將未追蹤檔案的內容加入至變更暫存集"
 
-#: add-interactive.c
+#: add-interactive.c add-interactive.c
 msgid "Prompt help:"
 msgstr "提示說明:"
 
@@ -191,7 +191,7 @@
 msgid "select multiple ranges"
 msgstr "選取多個範圍"
 
-#: add-interactive.c
+#: add-interactive.c add-interactive.c
 msgid "select item based on unique prefix"
 msgstr "根據獨特前綴選取項目"
 
@@ -231,9 +231,11 @@
 msgid "unstaged"
 msgstr "未暫存"
 
-#: add-interactive.c apply.c builtin/am.c builtin/bugreport.c builtin/clone.c
-#: builtin/diagnose.c builtin/fetch.c builtin/hook.c builtin/merge.c
-#: builtin/pull.c builtin/submodule--helper.c
+#: add-interactive.c apply.c apply.c builtin/am.c
+#: builtin/am.c builtin/bugreport.c builtin/clone.c
+#: builtin/diagnose.c builtin/fetch.c builtin/hook.c
+#: builtin/merge.c builtin/pull.c builtin/submodule--helper.c
+#: builtin/submodule--helper.c
 msgid "path"
 msgstr "路徑"
 
@@ -248,31 +250,31 @@
 
 #: add-patch.c
 #, c-format
-msgid "Stage mode change [y,n,q,a,d%s,?]? "
-msgstr "暫存模式變更 [y,n,q,a,d%s,?]? "
+msgid "Stage mode change%s [y,n,q,a,d%s,?]? "
+msgstr "暫存模式變更%s [y,n,q,a,d%s,?]? "
 
 #: add-patch.c
 #, c-format
-msgid "Stage deletion [y,n,q,a,d%s,?]? "
-msgstr "暫存刪除動作 [y,n,q,a,d%s,?]? "
+msgid "Stage deletion%s [y,n,q,a,d%s,?]? "
+msgstr "暫存刪除動作%s [y,n,q,a,d%s,?]? "
 
 #: add-patch.c
 #, c-format
-msgid "Stage addition [y,n,q,a,d%s,?]? "
-msgstr "暫存加入動作 [y,n,q,a,d%s,?]? "
+msgid "Stage addition%s [y,n,q,a,d%s,?]? "
+msgstr "暫存加入動作%s [y,n,q,a,d%s,?]? "
 
 #: add-patch.c
 #, c-format
-msgid "Stage this hunk [y,n,q,a,d%s,?]? "
-msgstr "暫存此區塊 [y,n,q,a,d%s,?]? "
+msgid "Stage this hunk%s [y,n,q,a,d%s,?]? "
+msgstr "暫存此區塊%s [y,n,q,a,d%s,?]? "
 
-#: add-patch.c
+#: add-patch.c add-patch.c
 msgid ""
 "If the patch applies cleanly, the edited hunk will immediately be marked for "
 "staging."
 msgstr "如果修補檔能完全套用,編輯區塊將立即標記為暫存。"
 
-#: add-patch.c
+#: add-patch.c add-patch.c
 msgid ""
 "y - stage this hunk\n"
 "n - do not stage this hunk\n"
@@ -288,23 +290,23 @@
 
 #: add-patch.c
 #, c-format
-msgid "Stash mode change [y,n,q,a,d%s,?]? "
-msgstr "貯存模式變更 [y,n,q,a,d%s,?]? "
+msgid "Stash mode change%s [y,n,q,a,d%s,?]? "
+msgstr "貯存模式變更%s [y,n,q,a,d%s,?]? "
 
 #: add-patch.c
 #, c-format
-msgid "Stash deletion [y,n,q,a,d%s,?]? "
-msgstr "貯存刪除動作 [y,n,q,a,d%s,?]? "
+msgid "Stash deletion%s [y,n,q,a,d%s,?]? "
+msgstr "貯存刪除動作%s [y,n,q,a,d%s,?]? "
 
 #: add-patch.c
 #, c-format
-msgid "Stash addition [y,n,q,a,d%s,?]? "
-msgstr "貯存加入動作 [y,n,q,a,d%s,?]? "
+msgid "Stash addition%s [y,n,q,a,d%s,?]? "
+msgstr "貯存加入動作%s [y,n,q,a,d%s,?]? "
 
 #: add-patch.c
 #, c-format
-msgid "Stash this hunk [y,n,q,a,d%s,?]? "
-msgstr "貯存此區塊 [y,n,q,a,d%s,?]? "
+msgid "Stash this hunk%s [y,n,q,a,d%s,?]? "
+msgstr "貯存此區塊%s [y,n,q,a,d%s,?]? "
 
 #: add-patch.c
 msgid ""
@@ -328,23 +330,23 @@
 
 #: add-patch.c
 #, c-format
-msgid "Unstage mode change [y,n,q,a,d%s,?]? "
-msgstr "取消暫存模式變更 [y,n,q,a,d%s,?]? "
+msgid "Unstage mode change%s [y,n,q,a,d%s,?]? "
+msgstr "取消暫存模式變更%s [y,n,q,a,d%s,?]? "
 
 #: add-patch.c
 #, c-format
-msgid "Unstage deletion [y,n,q,a,d%s,?]? "
-msgstr "取消暫存刪除動作 [y,n,q,a,d%s,?]? "
+msgid "Unstage deletion%s [y,n,q,a,d%s,?]? "
+msgstr "取消暫存刪除動作%s [y,n,q,a,d%s,?]? "
 
 #: add-patch.c
 #, c-format
-msgid "Unstage addition [y,n,q,a,d%s,?]? "
-msgstr "取消暫存加入動作 [y,n,q,a,d%s,?]? "
+msgid "Unstage addition%s [y,n,q,a,d%s,?]? "
+msgstr "取消暫存加入動作%s [y,n,q,a,d%s,?]? "
 
 #: add-patch.c
 #, c-format
-msgid "Unstage this hunk [y,n,q,a,d%s,?]? "
-msgstr "取消暫存此區塊 [y,n,q,a,d%s,?]? "
+msgid "Unstage this hunk%s [y,n,q,a,d%s,?]? "
+msgstr "取消暫存此區塊%s [y,n,q,a,d%s,?]? "
 
 #: add-patch.c
 msgid ""
@@ -368,25 +370,25 @@
 
 #: add-patch.c
 #, c-format
-msgid "Apply mode change to index [y,n,q,a,d%s,?]? "
-msgstr "將模式變更套用至索引區 [y,n,q,a,d%s,?]? "
+msgid "Apply mode change to index%s [y,n,q,a,d%s,?]? "
+msgstr "將模式變更套用至索引區%s [y,n,q,a,d%s,?]? "
 
 #: add-patch.c
 #, c-format
-msgid "Apply deletion to index [y,n,q,a,d%s,?]? "
-msgstr "將刪除動作套用至索引 [y,n,q,a,d%s,?]? "
+msgid "Apply deletion to index%s [y,n,q,a,d%s,?]? "
+msgstr "將刪除動作套用至索引%s [y,n,q,a,d%s,?]? "
 
 #: add-patch.c
 #, c-format
-msgid "Apply addition to index [y,n,q,a,d%s,?]? "
-msgstr "將加入動作套用至索引 [y,n,q,a,d%s,?]? "
+msgid "Apply addition to index%s [y,n,q,a,d%s,?]? "
+msgstr "將加入動作套用至索引%s [y,n,q,a,d%s,?]? "
 
 #: add-patch.c
 #, c-format
-msgid "Apply this hunk to index [y,n,q,a,d%s,?]? "
-msgstr "將此區塊套用到索引 [y,n,q,a,d%s,?]? "
+msgid "Apply this hunk to index%s [y,n,q,a,d%s,?]? "
+msgstr "將此區塊套用到索引%s [y,n,q,a,d%s,?]? "
 
-#: add-patch.c
+#: add-patch.c add-patch.c add-patch.c
 msgid ""
 "If the patch applies cleanly, the edited hunk will immediately be marked for "
 "applying."
@@ -406,33 +408,33 @@
 "a - 套用此區塊和本檔案中後面的全部區塊\n"
 "d - 不要套用此區塊和本檔案中後面的全部區塊\n"
 
-#: add-patch.c
+#: add-patch.c add-patch.c
 #, c-format
-msgid "Discard mode change from worktree [y,n,q,a,d%s,?]? "
-msgstr "從工作區捨棄模式變更 [y,n,q,a,d%s,?]? "
+msgid "Discard mode change from worktree%s [y,n,q,a,d%s,?]? "
+msgstr "從工作區捨棄模式變更%s [y,n,q,a,d%s,?]? "
 
-#: add-patch.c
+#: add-patch.c add-patch.c
 #, c-format
-msgid "Discard deletion from worktree [y,n,q,a,d%s,?]? "
-msgstr "從工作區捨棄刪除動作 [y,n,q,a,d%s,?]? "
+msgid "Discard deletion from worktree%s [y,n,q,a,d%s,?]? "
+msgstr "從工作區捨棄刪除動作%s [y,n,q,a,d%s,?]? "
 
-#: add-patch.c
+#: add-patch.c add-patch.c
 #, c-format
-msgid "Discard addition from worktree [y,n,q,a,d%s,?]? "
-msgstr "從工作區捨棄加入動作 [y,n,q,a,d%s,?]? "
+msgid "Discard addition from worktree%s [y,n,q,a,d%s,?]? "
+msgstr "從工作區捨棄加入動作%s [y,n,q,a,d%s,?]? "
 
-#: add-patch.c
+#: add-patch.c add-patch.c
 #, c-format
-msgid "Discard this hunk from worktree [y,n,q,a,d%s,?]? "
-msgstr "從工作區捨棄此區塊 [y,n,q,a,d%s,?]? "
+msgid "Discard this hunk from worktree%s [y,n,q,a,d%s,?]? "
+msgstr "從工作區捨棄此區塊%s [y,n,q,a,d%s,?]? "
 
-#: add-patch.c
+#: add-patch.c add-patch.c add-patch.c
 msgid ""
 "If the patch applies cleanly, the edited hunk will immediately be marked for "
 "discarding."
 msgstr "如果修補檔能完全套用,編輯區塊將立即標記為捨棄。"
 
-#: add-patch.c
+#: add-patch.c add-patch.c
 msgid ""
 "y - discard this hunk from worktree\n"
 "n - do not discard this hunk from worktree\n"
@@ -448,23 +450,23 @@
 
 #: add-patch.c
 #, c-format
-msgid "Discard mode change from index and worktree [y,n,q,a,d%s,?]? "
-msgstr "從索引區和工作區捨棄模式變更 [y,n,q,a,d%s,?]? "
+msgid "Discard mode change from index and worktree%s [y,n,q,a,d%s,?]? "
+msgstr "從索引區和工作區捨棄模式變更%s [y,n,q,a,d%s,?]? "
 
 #: add-patch.c
 #, c-format
-msgid "Discard deletion from index and worktree [y,n,q,a,d%s,?]? "
-msgstr "從索引和工作區捨棄刪除 [y,n,q,a,d%s,?]? "
+msgid "Discard deletion from index and worktree%s [y,n,q,a,d%s,?]? "
+msgstr "從索引和工作區捨棄刪除動作%s [y,n,q,a,d%s,?]? "
 
 #: add-patch.c
 #, c-format
-msgid "Discard addition from index and worktree [y,n,q,a,d%s,?]? "
-msgstr "從索引和工作區捨棄加入動作 [y,n,q,a,d%s,?]? "
+msgid "Discard addition from index and worktree%s [y,n,q,a,d%s,?]? "
+msgstr "從索引和工作區捨棄加入動作%s [y,n,q,a,d%s,?]? "
 
 #: add-patch.c
 #, c-format
-msgid "Discard this hunk from index and worktree [y,n,q,a,d%s,?]? "
-msgstr "從索引和工作區捨棄此區塊 [y,n,q,a,d%s,?]? "
+msgid "Discard this hunk from index and worktree%s [y,n,q,a,d%s,?]? "
+msgstr "從索引和工作區捨棄此區塊%s [y,n,q,a,d%s,?]? "
 
 #: add-patch.c
 msgid ""
@@ -482,23 +484,23 @@
 
 #: add-patch.c
 #, c-format
-msgid "Apply mode change to index and worktree [y,n,q,a,d%s,?]? "
-msgstr "將模式變更套用至索引區和工作區 [y,n,q,a,d%s,?]? "
+msgid "Apply mode change to index and worktree%s [y,n,q,a,d%s,?]? "
+msgstr "將模式變更套用至索引區和工作區%s [y,n,q,a,d%s,?]? "
 
 #: add-patch.c
 #, c-format
-msgid "Apply deletion to index and worktree [y,n,q,a,d%s,?]? "
-msgstr "將刪除動作套用到索引和工作區 [y,n,q,a,d%s,?]? "
+msgid "Apply deletion to index and worktree%s [y,n,q,a,d%s,?]? "
+msgstr "將刪除動作套用到索引和工作區%s [y,n,q,a,d%s,?]? "
 
 #: add-patch.c
 #, c-format
-msgid "Apply addition to index and worktree [y,n,q,a,d%s,?]? "
-msgstr "將加入動作套用到索引和工作區 [y,n,q,a,d%s,?]? "
+msgid "Apply addition to index and worktree%s [y,n,q,a,d%s,?]? "
+msgstr "將加入動作套用到索引和工作區%s [y,n,q,a,d%s,?]? "
 
 #: add-patch.c
 #, c-format
-msgid "Apply this hunk to index and worktree [y,n,q,a,d%s,?]? "
-msgstr "將此區塊套用到索引和工作區 [y,n,q,a,d%s,?]? "
+msgid "Apply this hunk to index and worktree%s [y,n,q,a,d%s,?]? "
+msgstr "將此區塊套用到索引和工作區%s [y,n,q,a,d%s,?]? "
 
 #: add-patch.c
 msgid ""
@@ -516,23 +518,23 @@
 
 #: add-patch.c
 #, c-format
-msgid "Apply mode change to worktree [y,n,q,a,d%s,?]? "
-msgstr "將模式變更套用至工作區 [y,n,q,a,d%s,?]? "
+msgid "Apply mode change to worktree%s [y,n,q,a,d%s,?]? "
+msgstr "將模式變更套用至工作區%s [y,n,q,a,d%s,?]? "
 
 #: add-patch.c
 #, c-format
-msgid "Apply deletion to worktree [y,n,q,a,d%s,?]? "
-msgstr "將刪除動作套用到工作區 [y,n,q,a,d%s,?]? "
+msgid "Apply deletion to worktree%s [y,n,q,a,d%s,?]? "
+msgstr "將刪除動作套用到工作區%s [y,n,q,a,d%s,?]? "
 
 #: add-patch.c
 #, c-format
-msgid "Apply addition to worktree [y,n,q,a,d%s,?]? "
-msgstr "將加入動作套用到工作區 [y,n,q,a,d%s,?]? "
+msgid "Apply addition to worktree%s [y,n,q,a,d%s,?]? "
+msgstr "將加入動作套用到工作區%s [y,n,q,a,d%s,?]? "
 
 #: add-patch.c
 #, c-format
-msgid "Apply this hunk to worktree [y,n,q,a,d%s,?]? "
-msgstr "將此區塊套用到工作區 [y,n,q,a,d%s,?]? "
+msgid "Apply this hunk to worktree%s [y,n,q,a,d%s,?]? "
+msgstr "將此區塊套用到工作區%s [y,n,q,a,d%s,?]? "
 
 #: add-patch.c
 msgid ""
@@ -548,6 +550,11 @@
 "a - 套用此區塊和本檔案中後面的全部區塊\n"
 "d - 不要套用此區塊和本檔案中後面的全部區塊\n"
 
+#: add-patch.c add-patch.c add-patch.c add-patch.c
+#, c-format
+msgid "%s cannot be negative"
+msgstr "%s 不能是負數"
+
 #: add-patch.c
 #, c-format
 msgid "could not parse hunk header '%.*s'"
@@ -659,6 +666,7 @@
 msgstr "未套用。\n"
 
 #: add-patch.c
+#, c-format
 msgid ""
 "j - go to the next undecided hunk, roll over at the bottom\n"
 "J - go to the next hunk, roll over at the bottom\n"
@@ -670,19 +678,37 @@
 "e - manually edit the current hunk\n"
 "p - print the current hunk\n"
 "P - print the current hunk using the pager\n"
+"> - go to the next file, roll over at the bottom\n"
+"< - go to the previous file, roll over at the top\n"
 "? - print help\n"
+"HUNKS SUMMARY - Hunks: %d, USE: %d, SKIP: %d\n"
 msgstr ""
-"j - 前往下一個未決區塊,在結尾時回到開頭\n"
-"J - 前往下一個區塊,在結尾時回到開頭\n"
-"k - 前往上一個未決區塊,在開頭時回到結尾\n"
-"K - 前往上一個區塊,在開頭時回到結尾\n"
+"j - 前往下一個未決區塊,在最後一個時回到第一個未決區塊\n"
+"J - 前往下一個區塊,在最後一個時回到第一個區塊\n"
+"k - 前往上一個未決區塊,在第一個時回到最後一個未決區塊\n"
+"K - 前往上一個區塊,在第一個時回到最後一個區塊\n"
 "g - 選擇要跳轉至的區塊\n"
 "/ - 尋找符合提供之常規表示式的區塊\n"
 "s - 分割目前區塊為更小的區塊\n"
 "e - 手動編輯目前區塊\n"
 "p - 輸出目前區塊\n"
 "P - 分頁輸出目前區塊\n"
+"> - 前往下一個檔案,在最後一個時回到第一個檔案\n"
+"< - 前往上一個檔案,在第一個時回到最後一個檔案\n"
 "? - 顯示說明\n"
+"區塊摘要 - 區塊數:%d,選取數:%d,略過數:%d\n"
+
+#: add-patch.c
+msgid "'git apply' failed"
+msgstr "「git apply」失敗"
+
+#: add-patch.c
+msgid " (was: y)"
+msgstr " (原本是:y)"
+
+#: add-patch.c
+msgid " (was: n)"
+msgstr " (原本是:n)"
 
 #: add-patch.c
 #, c-format
@@ -690,10 +716,18 @@
 msgstr "預期收到一個字母,卻收到「%s」"
 
 #: add-patch.c
+msgid "No next file"
+msgstr "沒有下一個檔案"
+
+#: add-patch.c
+msgid "No previous file"
+msgstr "沒有上一個檔案"
+
+#: add-patch.c add-patch.c
 msgid "No other hunk"
 msgstr "沒有其他區塊"
 
-#: add-patch.c
+#: add-patch.c add-patch.c
 msgid "No other undecided hunk"
 msgstr "沒有其他未決區塊"
 
@@ -756,10 +790,6 @@
 msgstr "未知命令「%s」(使用「?」獲取幫助)"
 
 #: add-patch.c
-msgid "'git apply' failed"
-msgstr "「git apply」失敗"
-
-#: add-patch.c
 msgid "No changes."
 msgstr "沒有變更。"
 
@@ -767,6 +797,30 @@
 msgid "Only binary files changed."
 msgstr "只有二進位檔案變更了。"
 
+#: add-patch.c
+#, c-format
+msgid "Stage mode change [y,n,q,a,d%s,?]? "
+msgstr "暫存模式變更 [y,n,q,a,d%s,?]? "
+
+#: add-patch.c
+#, c-format
+msgid "Stage deletion [y,n,q,a,d%s,?]? "
+msgstr "暫存刪除動作 [y,n,q,a,d%s,?]? "
+
+#: add-patch.c
+#, c-format
+msgid "Stage addition [y,n,q,a,d%s,?]? "
+msgstr "暫存加入動作 [y,n,q,a,d%s,?]? "
+
+#: add-patch.c
+#, c-format
+msgid "Stage this hunk [y,n,q,a,d%s,?]? "
+msgstr "暫存此區塊 [y,n,q,a,d%s,?]? "
+
+#: add-patch.c
+msgid "Revision does not refer to a commit"
+msgstr "修訂版指向的不是提交"
+
 #: advice.c
 #, c-format
 msgid ""
@@ -940,8 +994,11 @@
 msgid "unclosed quote"
 msgstr "未閉合的引號"
 
-#: alias.c builtin/cat-file.c builtin/notes.c builtin/prune-packed.c
-#: builtin/receive-pack.c builtin/refs.c builtin/repo.c builtin/tag.c
+#: alias.c builtin/cat-file.c builtin/notes.c builtin/notes.c
+#: builtin/notes.c builtin/notes.c builtin/notes.c
+#: builtin/notes.c builtin/notes.c builtin/notes.c
+#: builtin/notes.c builtin/prune-packed.c builtin/receive-pack.c
+#: builtin/refs.c builtin/repo.c builtin/tag.c
 #: t/helper/test-pkt-line.c
 msgid "too many arguments"
 msgstr "引數過多"
@@ -956,20 +1013,37 @@
 msgid "unrecognized whitespace ignore option '%s'"
 msgstr "無法辨識空白字元忽略選項「%s」"
 
-#: apply.c archive.c builtin/add.c builtin/branch.c builtin/checkout-index.c
-#: builtin/checkout.c builtin/clean.c builtin/clone.c builtin/commit.c
-#: builtin/describe.c builtin/diff-tree.c builtin/difftool.c
-#: builtin/fast-export.c builtin/fetch.c builtin/help.c builtin/index-pack.c
-#: builtin/init-db.c builtin/log.c builtin/ls-files.c builtin/merge-base.c
-#: builtin/merge-tree.c builtin/merge.c builtin/rebase.c builtin/repack.c
-#: builtin/reset.c builtin/rev-parse.c builtin/show-branch.c builtin/stash.c
-#: builtin/submodule--helper.c builtin/tag.c builtin/worktree.c parse-options.c
-#: range-diff.c revision.c
+#: apply.c archive.c builtin/add.c builtin/add.c
+#: builtin/add.c builtin/add.c builtin/branch.c
+#: builtin/checkout-index.c builtin/checkout.c builtin/checkout.c
+#: builtin/checkout.c builtin/checkout.c builtin/checkout.c
+#: builtin/clean.c builtin/clone.c builtin/clone.c
+#: builtin/commit.c builtin/commit.c builtin/commit.c
+#: builtin/commit.c builtin/commit.c builtin/describe.c
+#: builtin/diff-tree.c builtin/difftool.c builtin/fast-export.c
+#: builtin/fetch.c builtin/fetch.c builtin/fetch.c
+#: builtin/fetch.c builtin/help.c builtin/index-pack.c
+#: builtin/init-db.c builtin/log.c builtin/log.c
+#: builtin/ls-files.c builtin/merge-base.c builtin/merge-base.c
+#: builtin/merge-tree.c builtin/merge.c builtin/merge.c
+#: builtin/rebase.c builtin/rebase.c builtin/rebase.c
+#: builtin/repack.c builtin/reset.c builtin/reset.c
+#: builtin/rev-parse.c builtin/rev-parse.c builtin/rev-parse.c
+#: builtin/show-branch.c builtin/show-branch.c builtin/stash.c
+#: builtin/stash.c builtin/submodule--helper.c
+#: builtin/submodule--helper.c builtin/tag.c builtin/tag.c
+#: builtin/worktree.c builtin/worktree.c builtin/worktree.c
+#: builtin/worktree.c builtin/worktree.c builtin/worktree.c
+#: parse-options.c parse-options.c range-diff.c revision.c
+#: revision.c revision.c revision.c revision.c
+#: revision.c revision.c revision.c revision.c
+#: revision.c revision.c revision.c revision.c
+#: revision.c
 #, c-format
 msgid "options '%s' and '%s' cannot be used together"
 msgstr "無法同時使用「%s」和「%s」選項"
 
-#: apply.c
+#: apply.c apply.c apply.c
 #, c-format
 msgid "'%s' outside a repository"
 msgstr "「%s」在版本庫之外"
@@ -994,8 +1068,14 @@
 
 #: apply.c
 #, c-format
-msgid "unable to find filename in patch at line %d"
-msgstr "無法在修補檔的第 %d 列找到檔案名稱"
+msgid "unable to find filename in patch at %s:%d"
+msgstr "無法在修補檔的 %s:%d 處找到檔案名稱"
+
+#: apply.c
+#, c-format
+msgid "git apply: bad git-diff - expected /dev/null, got %s at %s:%d"
+msgstr ""
+"git apply:無效的 git-diff — 應為 /dev/null,但在 %2$s:%3$d 處得到 %1$s"
 
 #: apply.c
 #, c-format
@@ -1004,6 +1084,16 @@
 
 #: apply.c
 #, c-format
+msgid "git apply: bad git-diff - inconsistent new filename at %s:%d"
+msgstr "git apply:無效的 git-diff — %s:%d 處的新檔案名稱不一致"
+
+#: apply.c
+#, c-format
+msgid "git apply: bad git-diff - inconsistent old filename at %s:%d"
+msgstr "git apply:無效的 git-diff — %s:%d 處的舊檔案名稱不一致"
+
+#: apply.c
+#, c-format
 msgid "git apply: bad git-diff - inconsistent new filename on line %d"
 msgstr "git apply:無效的 git-diff — 第 %d 列的新檔案名稱不一致"
 
@@ -1014,11 +1104,21 @@
 
 #: apply.c
 #, c-format
+msgid "git apply: bad git-diff - expected /dev/null at %s:%d"
+msgstr "git apply:無效的 git-diff — %s:%d 處預期是 /dev/null"
+
+#: apply.c
+#, c-format
 msgid "git apply: bad git-diff - expected /dev/null on line %d"
 msgstr "git apply:無效的 git-diff — 第 %d 列處預期是 /dev/null"
 
 #: apply.c
 #, c-format
+msgid "invalid mode at %s:%d: %s"
+msgstr "%s:%d 處包含無效檔案模式:%s"
+
+#: apply.c
+#, c-format
 msgid "invalid mode on line %d: %s"
 msgstr "第 %d 列包含無效檔案模式:%s"
 
@@ -1031,6 +1131,17 @@
 #, c-format
 msgid ""
 "git diff header lacks filename information when removing %d leading pathname "
+"component at %s:%d"
+msgid_plural ""
+"git diff header lacks filename information when removing %d leading pathname "
+"components at %s:%d"
+msgstr[0] ""
+"移除 %d 個前導路徑部分後,git diff 標頭缺少檔案名稱資訊(位置:%s:%d)"
+
+#: apply.c
+#, c-format
+msgid ""
+"git diff header lacks filename information when removing %d leading pathname "
 "component (line %d)"
 msgid_plural ""
 "git diff header lacks filename information when removing %d leading pathname "
@@ -1039,6 +1150,11 @@
 
 #: apply.c
 #, c-format
+msgid "git diff header lacks filename information at %s:%d"
+msgstr "git diff 標頭缺少檔案名稱資訊(位置:%s:%d)"
+
+#: apply.c
+#, c-format
 msgid "git diff header lacks filename information (line %d)"
 msgstr "git diff 標頭缺少檔案名稱資訊(第 %d 列)"
 
@@ -1049,8 +1165,8 @@
 
 #: apply.c
 #, c-format
-msgid "patch fragment without header at line %d: %.*s"
-msgstr "第 %d 列的修補檔區段沒有標頭:%.*s"
+msgid "patch fragment without header at %s:%d: %.*s"
+msgstr "%s:%d 處的修補檔區段沒有標頭:%.*s"
 
 #: apply.c
 msgid "new file depends on old contents"
@@ -1062,8 +1178,8 @@
 
 #: apply.c
 #, c-format
-msgid "corrupt patch at line %d"
-msgstr "修補檔在第 %d 列發現損壞"
+msgid "corrupt patch at %s:%d"
+msgstr "修補檔在 %s:%d 處發現損壞"
 
 #: apply.c
 #, c-format
@@ -1082,18 +1198,18 @@
 
 #: apply.c
 #, c-format
-msgid "corrupt binary patch at line %d: %.*s"
-msgstr "二進位修補檔在第 %d 列損壞:%.*s"
+msgid "corrupt binary patch at %s:%d: %.*s"
+msgstr "二進位修補檔在 %s:%d 處有損壞:%.*s"
 
 #: apply.c
 #, c-format
-msgid "unrecognized binary patch at line %d"
-msgstr "無法辨識第 %d 列的二進位修補檔"
+msgid "unrecognized binary patch at %s:%d"
+msgstr "無法辨識 %s:%d 處的二進位修補檔"
 
 #: apply.c
 #, c-format
-msgid "patch with only garbage at line %d"
-msgstr "修補檔案的第 %d 列只有垃圾資料"
+msgid "patch with only garbage at %s:%d"
+msgstr "修補檔案的 %s:%d 處只有垃圾資料"
 
 #: apply.c
 #, c-format
@@ -1181,7 +1297,8 @@
 msgid "cannot checkout %s"
 msgstr "無法簽出 %s"
 
-#: apply.c midx.c pack-mtimes.c pack-revindex.c setup.c
+#: apply.c apply.c apply.c midx.c pack-mtimes.c
+#: pack-revindex.c setup.c
 #, c-format
 msgid "failed to read %s"
 msgstr "無法讀取 %s"
@@ -1191,17 +1308,17 @@
 msgid "reading from '%s' beyond a symbolic link"
 msgstr "讀取符號連結背後的「%s」"
 
-#: apply.c
+#: apply.c apply.c
 #, c-format
 msgid "path %s has been renamed/deleted"
 msgstr "路徑 %s 已被重新命名或刪除"
 
-#: apply.c
+#: apply.c apply.c
 #, c-format
 msgid "%s: does not exist in index"
 msgstr "%s:不在索引中"
 
-#: apply.c
+#: apply.c apply.c apply.c
 #, c-format
 msgid "%s: does not match index"
 msgstr "%s:與索引不符"
@@ -1215,7 +1332,7 @@
 msgid "Performing three-way merge...\n"
 msgstr "正在進行三方合併……\n"
 
-#: apply.c
+#: apply.c apply.c
 #, c-format
 msgid "cannot read the current contents of '%s'"
 msgstr "無法讀取「%s」目前的內容"
@@ -1254,7 +1371,9 @@
 msgid "%s has type %o, expected %o"
 msgstr "%s 的類型是 %o,預期是 %o"
 
-#: apply.c builtin/fast-import.c read-cache.c
+#: apply.c apply.c builtin/fast-import.c
+#: builtin/fast-import.c read-cache.c read-cache.c
+#: read-cache.c
 #, c-format
 msgid "invalid path '%s'"
 msgstr "路徑「%s」無效"
@@ -1279,7 +1398,7 @@
 msgid "new mode (%o) of %s does not match old mode (%o) of %s"
 msgstr "%2$s 的新模式(%1$o)和 %4$s 的舊模式(%3$o)不符"
 
-#: apply.c
+#: apply.c apply.c
 #, c-format
 msgid "affected file '%s' is beyond a symbolic link"
 msgstr "受影響的檔案「%s」在符號連結後"
@@ -1339,12 +1458,12 @@
 msgid "unable to create backing store for newly created file %s"
 msgstr "無法對剛建立的檔案 %s 建立後端儲存"
 
-#: apply.c
+#: apply.c apply.c
 #, c-format
 msgid "unable to add cache entry for %s"
 msgstr "無法為 %s 加入快取項目"
 
-#: apply.c builtin/bisect.c builtin/gc.c
+#: apply.c builtin/bisect.c builtin/gc.c builtin/gc.c
 #, c-format
 msgid "failed to write to '%s'"
 msgstr "無法寫入「%s」"
@@ -1372,9 +1491,9 @@
 #, c-format
 msgid "Applying patch %%s with %d reject..."
 msgid_plural "Applying patch %%s with %d rejects..."
-msgstr[0] "套用 %%s 個修補檔,其中 %d 個被拒絕……"
+msgstr[0] "套用修補檔 %%s,其中 %d 個被拒絕……"
 
-#: apply.c
+#: apply.c apply.c apply.c
 #, c-format
 msgid "cannot open %s"
 msgstr "無法開啟 %s"
@@ -1409,6 +1528,16 @@
 
 #: apply.c
 #, c-format
+msgid "option -p expects a non-negative integer, got '%s'"
+msgstr "選項 -p 預期是一個非負數整數,但卻得到「%s」"
+
+#: apply.c
+#, c-format
+msgid "unable to normalize directory: '%s'"
+msgstr "無法標準化目錄:「%s」"
+
+#: apply.c
+#, c-format
 msgid "can't open patch '%s': %s"
 msgstr "無法開啟修補檔「%s」:%s"
 
@@ -1418,7 +1547,7 @@
 msgid_plural "squelched %d whitespace errors"
 msgstr[0] "抑制下仍有 %d 個空白字元誤用"
 
-#: apply.c
+#: apply.c apply.c
 #, c-format
 msgid "%d line adds whitespace errors."
 msgid_plural "%d lines add whitespace errors."
@@ -1518,8 +1647,9 @@
 msgid "ensure at least <n> lines of context match"
 msgstr "確保至少符合 <n> 列上下文"
 
-#: apply.c builtin/am.c builtin/interpret-trailers.c builtin/pack-objects.c
-#: builtin/rebase.c
+#: apply.c builtin/am.c builtin/am.c
+#: builtin/interpret-trailers.c builtin/interpret-trailers.c
+#: builtin/pack-objects.c builtin/rebase.c
 msgid "action"
 msgstr "動作"
 
@@ -1527,7 +1657,7 @@
 msgid "detect new or modified lines that have whitespace errors"
 msgstr "檢查新增和修改的列中間,是否有空白字元誤用"
 
-#: apply.c
+#: apply.c apply.c
 msgid "ignore changes in whitespace when finding context"
 msgstr "尋找上下文時忽略空白字元變更"
 
@@ -1582,6 +1712,7 @@
 msgstr "不支援的檔案模式:0%o (SHA1:%s)"
 
 #: archive-tar.c archive-zip.c builtin/pack-objects.c
+#: builtin/pack-objects.c
 #, c-format
 msgid "deflate error (%d)"
 msgstr "壓縮錯誤 (%d)"
@@ -1629,7 +1760,9 @@
 msgid "git archive --remote <repo> [--exec <cmd>] --list"
 msgstr "git archive --remote <repo> [--exec <cmd>] --list"
 
-#: archive.c builtin/fast-import.c builtin/gc.c builtin/notes.c builtin/tag.c
+#: archive.c archive.c builtin/fast-import.c
+#: builtin/fast-import.c builtin/gc.c builtin/notes.c
+#: builtin/tag.c
 #, c-format
 msgid "cannot read '%s'"
 msgstr "無法讀取「%s」"
@@ -1639,7 +1772,7 @@
 msgid "pathspec '%s' matches files outside the current directory"
 msgstr "符合路徑規格「%s」的檔案在目前目錄之外"
 
-#: archive.c builtin/add.c builtin/rm.c
+#: archive.c builtin/add.c builtin/add.c builtin/rm.c
 #, c-format
 msgid "pathspec '%s' did not match any files"
 msgstr "路徑規格「%s」未符合任何檔案"
@@ -1705,13 +1838,17 @@
 msgid "prepend prefix to each pathname in the archive"
 msgstr "為封存中的每個路徑名稱加上前綴"
 
-#: archive.c builtin/blame.c builtin/commit-tree.c builtin/config.c
-#: builtin/fast-export.c builtin/gc.c builtin/grep.c builtin/hash-object.c
-#: builtin/ls-files.c builtin/notes.c builtin/read-tree.c parse-options.h
+#: archive.c archive.c builtin/blame.c builtin/blame.c
+#: builtin/blame.c builtin/commit-tree.c builtin/config.c
+#: builtin/fast-export.c builtin/fast-export.c
+#: builtin/fast-export.c builtin/gc.c builtin/gc.c
+#: builtin/grep.c builtin/hash-object.c builtin/ls-files.c
+#: builtin/ls-files.c builtin/notes.c builtin/notes.c
+#: builtin/read-tree.c parse-options.h
 msgid "file"
 msgstr "file"
 
-#: archive.c
+#: archive.c archive.c
 msgid "add untracked file to archive"
 msgstr "將未追蹤檔案加進封存"
 
@@ -1731,8 +1868,8 @@
 msgid "report archived files on stderr"
 msgstr "在 stderr 上回報封存的檔案"
 
-#: archive.c builtin/clone.c builtin/fetch.c builtin/pack-objects.c
-#: builtin/pull.c
+#: archive.c builtin/clone.c builtin/fetch.c
+#: builtin/pack-objects.c builtin/pack-objects.c builtin/pull.c
 msgid "time"
 msgstr "time"
 
@@ -1748,7 +1885,8 @@
 msgid "list supported archive formats"
 msgstr "列出支援的封存格式"
 
-#: archive.c builtin/archive.c builtin/clone.c builtin/submodule--helper.c
+#: archive.c builtin/archive.c builtin/clone.c builtin/clone.c
+#: builtin/submodule--helper.c builtin/submodule--helper.c
 msgid "repo"
 msgstr "repo"
 
@@ -1756,7 +1894,8 @@
 msgid "retrieve the archive from remote repository <repo>"
 msgstr "從遠端版本庫 <repo> 擷取封存檔案"
 
-#: archive.c builtin/archive.c builtin/difftool.c builtin/notes.c
+#: archive.c builtin/archive.c builtin/difftool.c
+#: builtin/notes.c
 msgid "command"
 msgstr "command"
 
@@ -1768,10 +1907,18 @@
 msgid "Unexpected option --remote"
 msgstr "非預期選項 --remote"
 
-#: archive.c builtin/add.c builtin/checkout.c builtin/clone.c builtin/commit.c
-#: builtin/fast-export.c builtin/index-pack.c builtin/log.c builtin/reset.c
-#: builtin/rm.c builtin/stash.c builtin/worktree.c fetch-pack.c http-fetch.c
-#: revision.c
+#: archive.c builtin/add.c builtin/add.c builtin/add.c
+#: builtin/add.c builtin/add.c builtin/checkout.c
+#: builtin/checkout.c builtin/checkout.c builtin/checkout.c
+#: builtin/clone.c builtin/clone.c builtin/commit.c
+#: builtin/commit.c builtin/commit.c builtin/fast-export.c
+#: builtin/index-pack.c builtin/log.c builtin/reset.c
+#: builtin/reset.c builtin/reset.c builtin/reset.c
+#: builtin/reset.c builtin/rm.c builtin/stash.c
+#: builtin/stash.c builtin/stash.c builtin/stash.c
+#: builtin/stash.c builtin/stash.c builtin/stash.c
+#: builtin/worktree.c builtin/worktree.c fetch-pack.c
+#: http-fetch.c http-fetch.c revision.c
 #, c-format
 msgid "the option '%s' requires '%s'"
 msgstr "「%s」選項需要「%s」"
@@ -1850,8 +1997,10 @@
 msgid "unable to stat '%s'"
 msgstr "無法 stat「%s」"
 
-#: bisect.c builtin/cat-file.c builtin/index-pack.c builtin/notes.c
-#: builtin/pack-objects.c combine-diff.c object-file.c rerere.c
+#: bisect.c builtin/cat-file.c builtin/cat-file.c
+#: builtin/index-pack.c builtin/notes.c builtin/pack-objects.c
+#: builtin/pack-objects.c builtin/pack-objects.c combine-diff.c
+#: object-file.c object-file.c rerere.c
 #, c-format
 msgid "unable to read %s"
 msgstr "無法讀取 %s"
@@ -1986,9 +2135,14 @@
 msgid "--reverse and --first-parent together require specified latest commit"
 msgstr "--reverse 和 --first-parent 共用,需要指定最新的提交"
 
-#: blame.c builtin/bisect.c builtin/commit.c builtin/fast-export.c
-#: builtin/log.c builtin/merge.c builtin/pack-objects.c builtin/shortlog.c
-#: midx-write.c pack-bitmap.c remote.c sequencer.c submodule.c
+#: blame.c builtin/bisect.c builtin/bisect.c
+#: builtin/commit.c builtin/fast-export.c builtin/log.c
+#: builtin/log.c builtin/log.c builtin/log.c builtin/log.c
+#: builtin/merge.c builtin/pack-objects.c builtin/pack-objects.c
+#: builtin/pack-objects.c builtin/pack-objects.c
+#: builtin/shortlog.c midx-write.c pack-bitmap.c
+#: pack-bitmap.c remote.c sequencer.c sequencer.c
+#: submodule.c
 msgid "revision walk setup failed"
 msgstr "修訂版遍歷設定失敗"
 
@@ -2063,10 +2217,12 @@
 msgstr "未追蹤:「%s」引用有歧義"
 
 #  譯者:為保證在輸出中對齊,注意調整句中空格!
+#. #-#-#-#-#  branch.c.po  #-#-#-#-#
 #. TRANSLATORS: This is a line listing a remote with duplicate
 #. refspecs in the advice message below. For RTL languages you'll
 #. probably want to swap the "%s" and leading "  " space around.
 #.
+#. #-#-#-#-#  object-name.c.po  #-#-#-#-#
 #. TRANSLATORS: This is line item of ambiguous object output
 #. from describe_ambiguous_object() above. For RTL languages
 #. you'll probably want to swap the "%s" and leading " " space
@@ -2148,7 +2304,8 @@
 "並建立兩個分支間的追蹤關係,您可能需要使用 `git push -u`\n"
 "推送分支並設定和上游的關聯。"
 
-#: branch.c builtin/replace.c
+#: branch.c builtin/replace.c builtin/replace.c
+#: builtin/replace.c builtin/replace.c
 #, c-format
 msgid "not a valid object name: '%s'"
 msgstr "物件名稱無效:「%s」"
@@ -2177,7 +2334,7 @@
 "您可以使用「git checkout --no-recurse-submodules %s && git submodule update "
 "--init」命令嘗試更新子模組"
 
-#: branch.c
+#: branch.c branch.c
 #, c-format
 msgid "submodule '%s': cannot create branch '%s'"
 msgstr "「%s」子模組:無法建立「%s」分支"
@@ -2227,14 +2384,15 @@
 msgstr "下列路徑根據其中一個 .gitignore 檔案而被忽略:\n"
 
 #: builtin/add.c builtin/clean.c builtin/fetch.c builtin/mv.c
-#: builtin/prune-packed.c builtin/pull.c builtin/push.c builtin/remote.c
-#: builtin/rm.c builtin/send-pack.c builtin/sparse-checkout.c
+#: builtin/prune-packed.c builtin/pull.c builtin/push.c
+#: builtin/remote.c builtin/rm.c builtin/send-pack.c
+#: builtin/sparse-checkout.c
 msgid "dry run"
 msgstr "測試執行"
 
 #: builtin/add.c builtin/check-ignore.c builtin/commit.c
-#: builtin/count-objects.c builtin/fsck.c builtin/log.c builtin/mv.c
-#: builtin/read-tree.c builtin/refs.c
+#: builtin/count-objects.c builtin/fsck.c builtin/log.c
+#: builtin/mv.c builtin/read-tree.c builtin/refs.c
 msgid "be verbose"
 msgstr "詳細輸出"
 
@@ -2246,6 +2404,11 @@
 msgid "select hunks interactively"
 msgstr "互動式選取區塊"
 
+#: builtin/add.c builtin/checkout.c builtin/reset.c
+#: builtin/stash.c builtin/stash.c
+msgid "auto advance to the next file when selecting hunks interactively"
+msgstr "在互動式選取區塊時自動前往下一個檔案"
+
 #: builtin/add.c
 msgid "edit current diff and apply"
 msgstr "編輯目前差異並套用"
@@ -2342,8 +2505,10 @@
 msgid "adding files failed"
 msgstr "加入檔案失敗"
 
-#: builtin/add.c builtin/checkout.c builtin/commit.c builtin/reset.c
-#: builtin/stash.c
+#: builtin/add.c builtin/add.c builtin/checkout.c
+#: builtin/checkout.c builtin/commit.c builtin/commit.c
+#: builtin/reset.c builtin/reset.c builtin/stash.c
+#: builtin/stash.c builtin/stash.c builtin/stash.c
 #, c-format
 msgid "'%s' cannot be negative"
 msgstr "「%s」不能是負數"
@@ -2353,8 +2518,8 @@
 msgid "--chmod param '%s' must be either -x or +x"
 msgstr "--chmod 的參數「%s」必須是 -x 或 +x"
 
-#: builtin/add.c builtin/checkout.c builtin/commit.c builtin/reset.c
-#: builtin/rm.c builtin/stash.c
+#: builtin/add.c builtin/checkout.c builtin/commit.c
+#: builtin/reset.c builtin/rm.c builtin/stash.c
 #, c-format
 msgid "'%s' and pathspec arguments cannot be used together"
 msgstr "「%s」和路徑規格引數不得同時使用"
@@ -2368,15 +2533,19 @@
 msgid "Maybe you wanted to say 'git add .'?"
 msgstr "也許您想要執行的是「git add .」?"
 
-#: builtin/add.c builtin/check-ignore.c builtin/checkout.c builtin/clean.c
-#: builtin/commit.c builtin/diff-tree.c builtin/grep.c builtin/mv.c
-#: builtin/reset.c builtin/rm.c builtin/submodule--helper.c read-cache.c
-#: rerere.c submodule.c
+#: builtin/add.c builtin/check-ignore.c builtin/checkout.c
+#: builtin/checkout.c builtin/clean.c builtin/commit.c
+#: builtin/diff-tree.c builtin/grep.c builtin/mv.c
+#: builtin/reset.c builtin/rm.c builtin/submodule--helper.c
+#: builtin/submodule--helper.c read-cache.c read-cache.c
+#: rerere.c rerere.c rerere.c submodule.c
 msgid "index file corrupt"
 msgstr "索引檔案損壞"
 
-#: builtin/add.c builtin/am.c builtin/checkout.c builtin/clone.c
-#: builtin/commit.c builtin/stash.c merge.c rerere.c
+#: builtin/add.c builtin/am.c builtin/am.c builtin/checkout.c
+#: builtin/checkout.c builtin/clone.c builtin/commit.c
+#: builtin/commit.c builtin/commit.c builtin/history.c
+#: builtin/stash.c merge.c rerere.c
 msgid "unable to write new index file"
 msgstr "無法寫入新的索引檔案"
 
@@ -2385,14 +2554,18 @@
 msgid "bad action '%s' for '%s'"
 msgstr "「%s」動作對「%s」無效"
 
-#: builtin/am.c builtin/blame.c builtin/fetch.c builtin/pack-objects.c
-#: builtin/pull.c builtin/revert.c diff-merges.c diff.c environment.c
-#: gpg-interface.c ls-refs.c parallel-checkout.c sequencer.c setup.c
+#: builtin/am.c builtin/am.c builtin/am.c builtin/blame.c
+#: builtin/blame.c builtin/fetch.c builtin/pack-objects.c
+#: builtin/pack-objects.c builtin/pull.c builtin/pull.c
+#: builtin/pull.c builtin/revert.c diff-merges.c diff.c
+#: environment.c gpg-interface.c gpg-interface.c ls-refs.c
+#: parallel-checkout.c sequencer.c setup.c setup.c setup.c
 #, c-format
 msgid "invalid value for '%s': '%s'"
 msgstr "「%s」的值無效:「%s」"
 
 #: builtin/am.c builtin/commit.c builtin/merge.c sequencer.c
+#: sequencer.c sequencer.c sequencer.c trailer.c
 #, c-format
 msgid "could not read '%s'"
 msgstr "無法讀取「%s」"
@@ -2401,6 +2574,7 @@
 msgid "could not parse author script"
 msgstr "無法解析作者文稿"
 
+#: builtin/am.c builtin/am.c builtin/am.c builtin/am.c
 #: builtin/am.c builtin/replace.c commit.c sequencer.c
 #, c-format
 msgid "could not parse %s"
@@ -2425,12 +2599,14 @@
 msgid "fseek failed"
 msgstr "fseek 失敗"
 
-#: builtin/am.c builtin/rebase.c sequencer.c wrapper.c
+#: builtin/am.c builtin/am.c builtin/rebase.c sequencer.c
+#: wrapper.c wrapper.c
 #, c-format
 msgid "could not open '%s' for reading"
 msgstr "無法開啟「%s」進行讀取"
 
-#: builtin/am.c builtin/rebase.c editor.c sequencer.c wrapper.c
+#: builtin/am.c builtin/rebase.c editor.c sequencer.c
+#: wrapper.c wrapper.c
 #, c-format
 msgid "could not open '%s' for writing"
 msgstr "無法開啟「%s」進行寫入"
@@ -2448,7 +2624,7 @@
 msgid "invalid timestamp"
 msgstr "無效的時間戳"
 
-#: builtin/am.c
+#: builtin/am.c builtin/am.c
 msgid "invalid Date line"
 msgstr "無效的 Date 列"
 
@@ -2504,7 +2680,8 @@
 msgid "invalid ident line: %.*s"
 msgstr "ident 列無效:%.*s"
 
-#: builtin/am.c builtin/checkout.c builtin/clone.c commit-graph.c
+#: builtin/am.c builtin/checkout.c builtin/clone.c
+#: commit-graph.c
 #, c-format
 msgid "unable to parse commit %s"
 msgstr "無法解析 %s 提交"
@@ -2541,11 +2718,12 @@
 msgid "applying to an empty history"
 msgstr "正在套用至空白歷史記錄上"
 
-#: builtin/am.c builtin/commit.c builtin/merge.c builtin/replay.c sequencer.c
+#: builtin/am.c builtin/commit.c builtin/merge.c
+#: builtin/merge.c replay.c sequencer.c
 msgid "failed to write commit object"
 msgstr "無法寫入提交物件"
 
-#: builtin/am.c
+#: builtin/am.c builtin/am.c
 #, c-format
 msgid "cannot resume: %s does not exist."
 msgstr "無法繼續:%s 不存在。"
@@ -2587,7 +2765,7 @@
 msgid "Patch is empty."
 msgstr "修補檔空白。"
 
-#: builtin/am.c
+#: builtin/am.c builtin/am.c
 #, c-format
 msgid "Applying: %.*s"
 msgstr "套用:%.*s"
@@ -2630,12 +2808,13 @@
 "您應該對已經解決衝突的每一個檔案執行 `git add`,標記為已經完成。\n"
 "你可以對「由他們刪除」的檔案,執行 `git rm` 指令。"
 
-#: builtin/am.c builtin/reset.c
+#: builtin/am.c builtin/am.c builtin/am.c builtin/reset.c
+#: builtin/reset.c
 #, c-format
 msgid "Could not parse object '%s'."
 msgstr "無法解析物件「%s」。"
 
-#: builtin/am.c
+#: builtin/am.c builtin/am.c
 msgid "failed to clean index"
 msgstr "無法清除索引"
 
@@ -2676,8 +2855,8 @@
 msgid "allow fall back on 3way merging if needed"
 msgstr "必要時允許回復至三方合併"
 
-#: builtin/am.c builtin/init-db.c builtin/prune-packed.c builtin/repack.c
-#: builtin/stash.c
+#: builtin/am.c builtin/init-db.c builtin/prune-packed.c
+#: builtin/repack.c builtin/stash.c
 msgid "be quiet"
 msgstr "靜默模式"
 
@@ -2713,21 +2892,30 @@
 msgid "pass it through git-mailinfo"
 msgstr "透過 git-mailinfo 傳入"
 
+#: builtin/am.c builtin/am.c builtin/am.c builtin/am.c
+#: builtin/am.c builtin/am.c builtin/am.c builtin/am.c
 #: builtin/am.c
 msgid "pass it through git-apply"
 msgstr "透過 git-apply 傳入"
 
-#: builtin/am.c builtin/commit.c builtin/fmt-merge-msg.c builtin/grep.c
-#: builtin/merge.c builtin/pull.c builtin/rebase.c builtin/repack.c
-#: builtin/show-branch.c builtin/show-ref.c builtin/tag.c parse-options.h
+#: builtin/am.c builtin/commit.c builtin/fmt-merge-msg.c
+#: builtin/fmt-merge-msg.c builtin/grep.c builtin/merge.c
+#: builtin/pull.c builtin/pull.c builtin/pull.c
+#: builtin/rebase.c builtin/repack.c builtin/repack.c
+#: builtin/repack.c builtin/show-branch.c builtin/show-ref.c
+#: builtin/tag.c parse-options.h parse-options.h
+#: parse-options.h
 msgid "n"
 msgstr "n"
 
-#: builtin/am.c builtin/branch.c builtin/bugreport.c builtin/cat-file.c
-#: builtin/clone.c builtin/diagnose.c builtin/for-each-ref.c builtin/init-db.c
-#: builtin/ls-files.c builtin/ls-tree.c builtin/refs.c builtin/replace.c
-#: builtin/repo.c builtin/submodule--helper.c builtin/tag.c
-#: builtin/verify-tag.c
+#: builtin/am.c builtin/branch.c builtin/bugreport.c
+#: builtin/cat-file.c builtin/cat-file.c builtin/cat-file.c
+#: builtin/clone.c builtin/diagnose.c builtin/for-each-ref.c
+#: builtin/init-db.c builtin/ls-files.c builtin/ls-tree.c
+#: builtin/refs.c builtin/replace.c builtin/repo.c
+#: builtin/repo.c builtin/submodule--helper.c
+#: builtin/submodule--helper.c builtin/submodule--helper.c
+#: builtin/tag.c builtin/verify-tag.c
 msgid "format"
 msgstr "format"
 
@@ -2779,8 +2967,9 @@
 msgid "use current timestamp for author date"
 msgstr "把目前時間戳當作是作者日期"
 
-#: builtin/am.c builtin/commit-tree.c builtin/commit.c builtin/merge.c
-#: builtin/pull.c builtin/rebase.c builtin/revert.c builtin/tag.c
+#: builtin/am.c builtin/commit-tree.c builtin/commit.c
+#: builtin/merge.c builtin/pull.c builtin/rebase.c
+#: builtin/revert.c builtin/tag.c
 msgid "key-id"
 msgstr "key-id"
 
@@ -2871,6 +3060,12 @@
 msgid "Restrict the missing objects to the current sparse-checkout"
 msgstr "將缺少的物件限制於目前的稀疏簽出"
 
+#: builtin/backfill.c builtin/diff-pairs.c builtin/log.c
+#: builtin/log.c builtin/replay.c builtin/shortlog.c bundle.c
+#, c-format
+msgid "unrecognized argument: %s"
+msgstr "不認識的引數:%s"
+
 #: builtin/bisect.c
 msgid ""
 "git bisect start [--term-(bad|new)=<term-new> --term-(good|old)=<term-old>]\n"
@@ -3050,7 +3245,7 @@
 msgid "could not open '%s' for appending"
 msgstr "無法開啟「%s」進行附加"
 
-#: builtin/bisect.c
+#: builtin/bisect.c builtin/bisect.c
 msgid "'' is not a valid term"
 msgstr "「 」不是有效術語"
 
@@ -3104,7 +3299,7 @@
 msgid "'git bisect %s' can take only one argument."
 msgstr "「git bisect %s」只取一個引數。"
 
-#: builtin/bisect.c
+#: builtin/bisect.c builtin/bisect.c
 #, c-format
 msgid "Bad rev input: %s"
 msgstr "rev 輸入格式錯誤:%s"
@@ -3215,7 +3410,7 @@
 msgid "git annotate [<options>] [<rev-opts>] [<rev>] [--] <file>"
 msgstr "git annotate [<options>] [<rev-opts>] [<rev>] [--] <file>"
 
-#: builtin/blame.c
+#: builtin/blame.c builtin/blame.c
 msgid "<rev-opts> are documented in git-rev-list(1)"
 msgstr "<rev-opts> 的文件在 git-rev-list(1)"
 
@@ -3228,7 +3423,7 @@
 msgid "must end with a color"
 msgstr "結尾必須是一個顏色"
 
-#: builtin/blame.c diff.c merge-ort.c transport.c
+#: builtin/blame.c diff.c diff.c merge-ort.c transport.c
 #, c-format
 msgid "unknown value for config '%s': %s"
 msgstr "設定 '%s' 的取值未知:%s"
@@ -3261,8 +3456,9 @@
 msgid "show work cost statistics"
 msgstr "顯示工作量統計"
 
-#: builtin/blame.c builtin/checkout.c builtin/clone.c builtin/commit-graph.c
-#: builtin/fetch.c builtin/merge.c builtin/multi-pack-index.c builtin/pull.c
+#: builtin/blame.c builtin/checkout.c builtin/clone.c
+#: builtin/commit-graph.c builtin/commit-graph.c builtin/fetch.c
+#: builtin/merge.c builtin/multi-pack-index.c builtin/pull.c
 #: builtin/push.c builtin/remote.c builtin/send-pack.c
 msgid "force progress reporting"
 msgstr "強制顯示進度報告"
@@ -3351,7 +3547,7 @@
 msgid "use <file>'s contents as the final image"
 msgstr "將 <file> 的內容當成是最終印象"
 
-#: builtin/blame.c
+#: builtin/blame.c builtin/blame.c
 msgid "score"
 msgstr "score"
 
@@ -3543,12 +3739,12 @@
 msgid "invalid branch name: '%s'"
 msgstr "分支名稱無效:「%s」"
 
-#: builtin/branch.c
+#: builtin/branch.c builtin/branch.c builtin/branch.c
 #, c-format
 msgid "no commit on branch '%s' yet"
 msgstr "分支「%s」尚無提交"
 
-#: builtin/branch.c
+#: builtin/branch.c builtin/branch.c
 #, c-format
 msgid "no branch named '%s'"
 msgstr "沒有名為「%s」的分支"
@@ -3607,7 +3803,8 @@
 msgid "suppress informational messages"
 msgstr "不顯示資訊訊息"
 
-#: builtin/branch.c builtin/checkout.c builtin/submodule--helper.c
+#: builtin/branch.c builtin/checkout.c
+#: builtin/submodule--helper.c
 msgid "set branch tracking configuration"
 msgstr "設定分支追蹤組態"
 
@@ -3635,11 +3832,11 @@
 msgid "act on remote-tracking branches"
 msgstr "作用於遠端追蹤分支"
 
-#: builtin/branch.c
+#: builtin/branch.c builtin/branch.c
 msgid "print only branches that contain the commit"
 msgstr "只輸出包含此提交的分支"
 
-#: builtin/branch.c
+#: builtin/branch.c builtin/branch.c
 msgid "print only branches that don't contain the commit"
 msgstr "只輸出不包含此提交的分支"
 
@@ -3711,7 +3908,9 @@
 msgid "list branches in columns"
 msgstr "以行的形式列出分支"
 
-#: builtin/branch.c builtin/for-each-ref.c builtin/notes.c builtin/tag.c
+#: builtin/branch.c builtin/for-each-ref.c builtin/notes.c
+#: builtin/notes.c builtin/notes.c builtin/notes.c
+#: builtin/tag.c
 msgid "object"
 msgstr "object"
 
@@ -3727,8 +3926,8 @@
 msgid "recurse through submodules"
 msgstr "在子模組中遞迴"
 
-#: builtin/branch.c builtin/for-each-ref.c builtin/ls-files.c builtin/ls-tree.c
-#: builtin/tag.c builtin/verify-tag.c
+#: builtin/branch.c builtin/for-each-ref.c builtin/ls-files.c
+#: builtin/ls-tree.c builtin/tag.c builtin/verify-tag.c
 msgid "format to use for the output"
 msgstr "輸出格式"
 
@@ -3751,7 +3950,7 @@
 msgid "--recurse-submodules can only be used to create branches"
 msgstr "--recurse-submodules 只能用來建立分支"
 
-#: builtin/branch.c
+#: builtin/branch.c builtin/branch.c
 msgid "branch name required"
 msgstr "必須提供分支名稱"
 
@@ -3789,7 +3988,7 @@
 "could not set upstream of HEAD to %s when it does not point to any branch"
 msgstr "HEAD 不指向任何分支時無法將其上游設為 %s"
 
-#: builtin/branch.c
+#: builtin/branch.c builtin/branch.c
 #, c-format
 msgid "no such branch '%s'"
 msgstr "無「%s」分支"
@@ -3887,8 +4086,11 @@
 "請檢閱臭蟲報告下方的剩餘部分。\n"
 "您可刪除任何您不想分享的地方。\n"
 
-#: builtin/bugreport.c builtin/commit.c builtin/fast-export.c
-#: builtin/pack-objects.c builtin/rebase.c builtin/replay.c parse-options.h
+#: builtin/bugreport.c builtin/commit.c builtin/commit.c
+#: builtin/commit.c builtin/fast-export.c builtin/fast-export.c
+#: builtin/fast-export.c builtin/fast-export.c
+#: builtin/pack-objects.c builtin/rebase.c builtin/replay.c
+#: parse-options.h
 msgid "mode"
 msgstr "mode"
 
@@ -3966,7 +4168,7 @@
 msgid "do not show progress meter"
 msgstr "不顯示進度列"
 
-#: builtin/bundle.c builtin/pack-objects.c
+#: builtin/bundle.c builtin/bundle.c builtin/pack-objects.c
 msgid "show progress meter"
 msgstr "顯示進度列"
 
@@ -4180,7 +4382,8 @@
 msgid "path|tree-ish"
 msgstr "path|tree-ish"
 
-#: builtin/cat-file.c
+#: builtin/cat-file.c builtin/cat-file.c builtin/cat-file.c
+#: builtin/cat-file.c builtin/cat-file.c
 #, c-format
 msgid "'%s' requires a batch mode"
 msgstr "「%s」需要批次處理模式"
@@ -4194,7 +4397,7 @@
 msgid "batch modes take no arguments"
 msgstr "批次處理模式不取引數"
 
-#: builtin/cat-file.c
+#: builtin/cat-file.c builtin/cat-file.c
 #, c-format
 msgid "<rev> required with '%s'"
 msgstr "「%s」需要 <rev>"
@@ -4247,7 +4450,8 @@
 msgid "which tree-ish to check attributes at"
 msgstr "要檢查屬性的樹狀物件指示元"
 
-#: builtin/check-ignore.c builtin/checkout.c builtin/gc.c builtin/worktree.c
+#: builtin/check-ignore.c builtin/checkout.c builtin/gc.c
+#: builtin/worktree.c
 msgid "suppress progress reporting"
 msgstr "不顯示進度報告"
 
@@ -4311,8 +4515,10 @@
 msgid "git checkout--worker [<options>]"
 msgstr "git checkout--worker [<options>]"
 
-#: builtin/checkout--worker.c builtin/checkout-index.c builtin/column.c
+#: builtin/checkout--worker.c builtin/checkout-index.c
+#: builtin/column.c builtin/column.c builtin/submodule--helper.c
 #: builtin/submodule--helper.c builtin/worktree.c
+#: builtin/worktree.c
 msgid "string"
 msgstr "string"
 
@@ -4364,28 +4570,12 @@
 msgid "copy out the files from named stage"
 msgstr "從指定暫存區中拷出檔案"
 
-#: builtin/checkout.c
-msgid "git checkout [<options>] <branch>"
-msgstr "git checkout [<options>] <branch>"
-
-#: builtin/checkout.c
-msgid "git checkout [<options>] [<branch>] -- <file>..."
-msgstr "git checkout [<options>] [<branch>] -- <file>..."
-
-#: builtin/checkout.c
-msgid "git switch [<options>] [<branch>]"
-msgstr "git switch [<options>] [<branch>]"
-
-#: builtin/checkout.c
-msgid "git restore [<options>] [--source=<branch>] <file>..."
-msgstr "git restore [<options>] [--source=<branch>] <file>..."
-
-#: builtin/checkout.c
+#: builtin/checkout.c builtin/checkout.c
 #, c-format
 msgid "path '%s' does not have our version"
 msgstr "「%s」路徑沒有我們的版本"
 
-#: builtin/checkout.c
+#: builtin/checkout.c builtin/checkout.c
 #, c-format
 msgid "path '%s' does not have their version"
 msgstr "「%s」路徑沒有他們的版本"
@@ -4428,6 +4618,7 @@
 msgid_plural "Updated %d paths from the index"
 msgstr[0] "已從索引區更新 %d 個路徑"
 
+#: builtin/checkout.c builtin/checkout.c builtin/checkout.c
 #: builtin/checkout.c
 #, c-format
 msgid "'%s' cannot be used with updating paths"
@@ -4448,7 +4639,7 @@
 msgid "'%s' must be used when '%s' is not specified"
 msgstr "未指定「%2$s」時,必須使用「%1$s」"
 
-#: builtin/checkout.c
+#: builtin/checkout.c builtin/checkout.c
 #, c-format
 msgid "'%s' or '%s' cannot be used with %s"
 msgstr "「%s」或「%s」不能與 %s 同時使用"
@@ -4458,13 +4649,16 @@
 msgid "'%s', '%s', or '%s' cannot be used when checking out of a tree"
 msgstr "「%s」、「%s」或「%s」不能在簽出樹狀物件時使用"
 
-#: builtin/checkout.c
+#: builtin/checkout.c builtin/checkout.c
 #, c-format
 msgid "path '%s' is unmerged"
 msgstr "路徑「%s」未合併"
 
-#: builtin/checkout.c builtin/grep.c builtin/merge-tree.c builtin/reset.c
-#: merge-ort.c reset.c sequencer.c tree-walk.c
+#: builtin/checkout.c builtin/checkout.c builtin/checkout.c
+#: builtin/grep.c builtin/grep.c builtin/grep.c
+#: builtin/merge-tree.c builtin/merge-tree.c builtin/merge-tree.c
+#: builtin/reset.c merge-ort.c reset.c sequencer.c
+#: sequencer.c tree-walk.c
 #, c-format
 msgid "unable to read tree (%s)"
 msgstr "無法讀取樹狀物件(%s)"
@@ -4491,7 +4685,7 @@
 msgid "HEAD is now at"
 msgstr "HEAD 目前位於"
 
-#: builtin/checkout.c builtin/clone.c
+#: builtin/checkout.c builtin/clone.c builtin/clone.c
 msgid "unable to update HEAD"
 msgstr "無法更新 HEAD"
 
@@ -4510,7 +4704,7 @@
 msgid "Switched to and reset branch '%s'\n"
 msgstr "已切換並重設分支「%s」\n"
 
-#: builtin/checkout.c
+#: builtin/checkout.c builtin/checkout.c
 #, c-format
 msgid "Switched to a new branch '%s'\n"
 msgstr "已切換至新分支「%s」\n"
@@ -4573,7 +4767,7 @@
 msgid "Previous HEAD position was"
 msgstr "之前的 HEAD 指標位置在"
 
-#: builtin/checkout.c
+#: builtin/checkout.c builtin/checkout.c
 msgid "You are on a branch yet to be born"
 msgstr "您正位於一個尚未初始化的分支"
 
@@ -4587,11 +4781,12 @@
 "請使用 --(和可選的 --no-guess)來消除歧義"
 
 #: builtin/checkout.c
+#, c-format
 msgid ""
 "If you meant to check out a remote tracking branch on, e.g. 'origin',\n"
 "you can do so by fully qualifying the name with the --track option:\n"
 "\n"
-"    git checkout --track origin/<name>\n"
+"    git %s --track origin/<name>\n"
 "\n"
 "If you'd like to always have checkouts of an ambiguous <name> prefer\n"
 "one remote, e.g. the 'origin' remote, consider setting\n"
@@ -4600,7 +4795,7 @@
 "如果您想要簽出遠端追蹤分支,例如「origin」,\n"
 "您可以使用 --track 選項寫出全名:\n"
 "\n"
-"    git checkout --track origin/<name>\n"
+"    git %s --track origin/<name>\n"
 "\n"
 "如果您平時比較想要使用模糊的簡短分支名稱 <name>,\n"
 "而不太想寫如「origin」的遠端版本庫名稱,\n"
@@ -4620,12 +4815,12 @@
 msgid "only one reference expected, %d given."
 msgstr "預期只有一個引用,卻提供了 %d 個。"
 
-#: builtin/checkout.c builtin/worktree.c
+#: builtin/checkout.c builtin/worktree.c builtin/worktree.c
 #, c-format
 msgid "invalid reference: %s"
 msgstr "無效引用:%s"
 
-#: builtin/checkout.c
+#: builtin/checkout.c builtin/checkout.c
 #, c-format
 msgid "reference is not a tree: %s"
 msgstr "引用不是樹狀物件:%s"
@@ -4640,7 +4835,7 @@
 msgid "a branch is expected, got remote branch '%s'"
 msgstr "預期收到分支,卻收到遠端分支「%s」"
 
-#: builtin/checkout.c
+#: builtin/checkout.c builtin/checkout.c
 #, c-format
 msgid "a branch is expected, got '%s'"
 msgstr "預期收到分支,卻收到「%s」"
@@ -4703,7 +4898,7 @@
 msgid "paths cannot be used with switching branches"
 msgstr "切換分支時不能指定路徑"
 
-#: builtin/checkout.c
+#: builtin/checkout.c builtin/checkout.c builtin/checkout.c
 #, c-format
 msgid "'%s' cannot be used with switching branches"
 msgstr "切換分支時不能指定「%s」"
@@ -4713,7 +4908,8 @@
 msgid "'%s' needs the paths to check out"
 msgstr "「%s」需要指定要簽出的路徑"
 
-#: builtin/checkout.c
+#: builtin/checkout.c builtin/checkout.c builtin/checkout.c
+#: builtin/checkout.c builtin/checkout.c
 #, c-format
 msgid "'%s' cannot be used with '%s'"
 msgstr "「%s」不能與「%s」同時使用"
@@ -4741,7 +4937,8 @@
 msgid "perform a 3-way merge with the new branch"
 msgstr "和新分支進行三方合併"
 
-#: builtin/checkout.c builtin/log.c builtin/range-diff.c parse-options.h
+#: builtin/checkout.c builtin/log.c builtin/range-diff.c
+#: parse-options.h
 msgid "style"
 msgstr "style"
 
@@ -4786,6 +4983,22 @@
 msgstr "不將路徑規格限制為僅稀疏項目"
 
 #: builtin/checkout.c
+msgid "git checkout [<options>] <branch>"
+msgstr "git checkout [<options>] <branch>"
+
+#: builtin/checkout.c
+msgid "git checkout [<options>] [<branch>] -- <file>..."
+msgstr "git checkout [<options>] [<branch>] -- <file>..."
+
+#: builtin/checkout.c
+msgid "git switch [<options>] [<branch>]"
+msgstr "git switch [<options>] [<branch>]"
+
+#: builtin/checkout.c
+msgid "git restore [<options>] [--source=<branch>] <file>..."
+msgstr "git restore [<options>] [--source=<branch>] <file>..."
+
+#: builtin/checkout.c
 #, c-format
 msgid "options '-%c', '-%c', and '%s' cannot be used together"
 msgstr "「-%c」、「-%c」和「%s」選項不能同時使用"
@@ -4829,8 +5042,12 @@
 msgid "you must specify path(s) to restore"
 msgstr "您必須指定要還原的路徑"
 
-#: builtin/checkout.c builtin/clone.c builtin/remote.c builtin/replay.c
+#: builtin/checkout.c builtin/checkout.c builtin/checkout.c
+#: builtin/checkout.c builtin/clone.c builtin/remote.c
+#: builtin/remote.c builtin/replay.c builtin/replay.c
+#: builtin/replay.c builtin/submodule--helper.c
 #: builtin/submodule--helper.c builtin/worktree.c
+#: builtin/worktree.c
 msgid "branch"
 msgstr "branch"
 
@@ -5038,9 +5255,11 @@
 msgid "remove whole directories"
 msgstr "移除整個目錄"
 
-#: builtin/clean.c builtin/config.c builtin/describe.c builtin/grep.c
-#: builtin/log.c builtin/ls-files.c builtin/name-rev.c builtin/show-ref.c
-#: pack-refs.c ref-filter.h
+#: builtin/clean.c builtin/config.c builtin/config.c
+#: builtin/config.c builtin/describe.c builtin/describe.c
+#: builtin/grep.c builtin/log.c builtin/log.c
+#: builtin/ls-files.c builtin/name-rev.c builtin/name-rev.c
+#: builtin/show-ref.c pack-refs.c pack-refs.c ref-filter.h
 msgid "pattern"
 msgstr "pattern"
 
@@ -5065,7 +5284,8 @@
 msgid "info: Could not add alternate for '%s': %s\n"
 msgstr "info: 不能為「%s」新增一個備用:%s\n"
 
-#: builtin/clone.c builtin/diff.c builtin/rm.c grep.c setup.c
+#: builtin/clone.c builtin/diff.c builtin/rm.c grep.c
+#: setup.c
 #, c-format
 msgid "failed to stat '%s'"
 msgstr "對「%s」呼叫 stat 失敗"
@@ -5176,7 +5396,7 @@
 msgid "don't create a checkout"
 msgstr "不要建立簽出"
 
-#: builtin/clone.c builtin/init-db.c
+#: builtin/clone.c builtin/clone.c builtin/init-db.c
 msgid "create a bare repository"
 msgstr "建立裸版本庫"
 
@@ -5216,17 +5436,21 @@
 msgid "directory from which templates will be used"
 msgstr "將被使用的模板目錄"
 
-#: builtin/clone.c builtin/submodule--helper.c
+#: builtin/clone.c builtin/clone.c builtin/submodule--helper.c
+#: builtin/submodule--helper.c builtin/submodule--helper.c
 msgid "reference repository"
 msgstr "引用版本庫"
 
 #: builtin/clone.c builtin/submodule--helper.c
+#: builtin/submodule--helper.c
 msgid "use --reference only while cloning"
 msgstr "僅在拓製時使用 --reference"
 
-#: builtin/clone.c builtin/column.c builtin/fmt-merge-msg.c builtin/init-db.c
-#: builtin/merge-file.c builtin/merge.c builtin/pack-objects.c builtin/repack.c
+#: builtin/clone.c builtin/column.c builtin/fmt-merge-msg.c
+#: builtin/init-db.c builtin/merge-file.c builtin/merge.c
+#: builtin/pack-objects.c builtin/repack.c
 #: builtin/submodule--helper.c t/helper/test-simple-ipc.c
+#: t/helper/test-simple-ipc.c
 msgid "name"
 msgstr "name"
 
@@ -5267,6 +5491,7 @@
 msgstr "取得更多淺層拓製的過往歷史記錄,除了特定修訂版"
 
 #: builtin/clone.c builtin/submodule--helper.c
+#: builtin/submodule--helper.c
 msgid "clone only one branch, HEAD or --branch"
 msgstr "只拓製一個分支、HEAD 或 --branch"
 
@@ -5287,6 +5512,7 @@
 msgstr "git 目錄和工作區分離"
 
 #: builtin/clone.c builtin/init-db.c builtin/submodule--helper.c
+#: builtin/submodule--helper.c builtin/submodule--helper.c
 msgid "specify the reference format to use"
 msgstr "指定要使用的引用格式"
 
@@ -5298,13 +5524,13 @@
 msgid "set config inside the new repository"
 msgstr "在新版本庫中設定設定訊息"
 
-#: builtin/clone.c builtin/fetch.c builtin/ls-remote.c builtin/pull.c
-#: builtin/push.c builtin/send-pack.c
+#: builtin/clone.c builtin/fetch.c builtin/ls-remote.c
+#: builtin/pull.c builtin/push.c builtin/send-pack.c
 msgid "server-specific"
 msgstr "server-specific"
 
-#: builtin/clone.c builtin/fetch.c builtin/ls-remote.c builtin/pull.c
-#: builtin/push.c builtin/send-pack.c
+#: builtin/clone.c builtin/fetch.c builtin/ls-remote.c
+#: builtin/pull.c builtin/push.c builtin/send-pack.c
 msgid "option to transmit"
 msgstr "傳輸選項"
 
@@ -5340,8 +5566,9 @@
 msgid "You must specify a repository to clone."
 msgstr "您必須指定要拓製的版本庫。"
 
-#: builtin/clone.c builtin/init-db.c builtin/refs.c builtin/submodule--helper.c
-#: setup.c
+#: builtin/clone.c builtin/init-db.c builtin/refs.c
+#: builtin/submodule--helper.c builtin/submodule--helper.c
+#: builtin/submodule--helper.c setup.c setup.c
 #, c-format
 msgid "unknown ref storage format '%s'"
 msgstr "未知的引用儲存格式「%s」"
@@ -5371,7 +5598,8 @@
 msgid "working tree '%s' already exists."
 msgstr "工作區 '%s' 已經存在。"
 
-#: builtin/clone.c builtin/difftool.c builtin/log.c builtin/worktree.c
+#: builtin/clone.c builtin/clone.c builtin/difftool.c
+#: builtin/log.c builtin/worktree.c builtin/worktree.c
 #, c-format
 msgid "could not create leading directories of '%s'"
 msgstr "無法為「%s」建立前導目錄"
@@ -5397,7 +5625,7 @@
 "able"
 msgstr "clone --recursive 與 --reference 和 --reference-if-able 不相容"
 
-#: builtin/clone.c builtin/remote.c
+#: builtin/clone.c builtin/remote.c builtin/remote.c
 #, c-format
 msgid "'%s' is not a valid remote name"
 msgstr "'%s' 不是一個有效的遠端名稱"
@@ -5418,7 +5646,7 @@
 msgid "--filter is ignored in local clones; use file:// instead."
 msgstr "本機拓製會忽略 --filter。請改用 file:// 協定。"
 
-#: builtin/clone.c fetch-pack.c
+#: builtin/clone.c fetch-pack.c fetch-pack.c
 msgid "source repository is shallow, reject to clone."
 msgstr "來源版本庫是淺版本庫 (shallow)。拒絕拓製。"
 
@@ -5434,7 +5662,7 @@
 msgid "cannot clone from filtered bundle"
 msgstr "無法從過濾後的套件包拓製"
 
-#: builtin/clone.c
+#: builtin/clone.c builtin/clone.c
 msgid "failed to initialize the repo, skipping bundle URI"
 msgstr "無法初始化版本庫,略過套件包 URI"
 
@@ -5447,7 +5675,7 @@
 msgid "failed to fetch advertised bundles"
 msgstr "無法抓取公佈的套件包"
 
-#: builtin/clone.c
+#: builtin/clone.c builtin/clone.c
 msgid "remote transport reported error"
 msgstr "遠端傳輸回報錯誤"
 
@@ -5473,7 +5701,7 @@
 msgid "lookup config vars"
 msgstr "尋找設定變數"
 
-#: builtin/column.c
+#: builtin/column.c builtin/column.c
 msgid "layout to use"
 msgstr "要使用的配置"
 
@@ -5524,8 +5752,8 @@
 "[no-]progress]\n"
 "                       <split-options>"
 
-#: builtin/commit-graph.c builtin/fetch.c builtin/gc.c builtin/log.c
-#: builtin/repack.c
+#: builtin/commit-graph.c builtin/fetch.c builtin/gc.c
+#: builtin/log.c builtin/repack.c builtin/repack.c
 msgid "dir"
 msgstr "目錄"
 
@@ -5562,7 +5790,7 @@
 msgid "invalid object: %s"
 msgstr "物件無效:%s"
 
-#: builtin/commit-graph.c parse-options-cb.c
+#: builtin/commit-graph.c parse-options-cb.c parse-options-cb.c
 #, c-format
 msgid "option `%s' expects a numerical value"
 msgstr "選項 `%s' 期望一個數字值"
@@ -5632,7 +5860,7 @@
 msgid "duplicate parent %s ignored"
 msgstr "忽略重複的父提交 %s"
 
-#: builtin/commit-tree.c builtin/log.c
+#: builtin/commit-tree.c builtin/commit-tree.c builtin/log.c
 #, c-format
 msgid "not a valid object name %s"
 msgstr "不是一個有效的物件名 %s"
@@ -5655,8 +5883,9 @@
 msgid "id of a parent commit object"
 msgstr "父提交物件 ID"
 
-#: builtin/commit-tree.c builtin/commit.c builtin/merge.c builtin/notes.c
-#: builtin/stash.c builtin/tag.c
+#: builtin/commit-tree.c builtin/commit.c builtin/merge.c
+#: builtin/notes.c builtin/notes.c builtin/stash.c
+#: builtin/tag.c
 msgid "message"
 msgstr "訊息"
 
@@ -5668,8 +5897,8 @@
 msgid "read commit log message from file"
 msgstr "從檔案中讀取提交說明"
 
-#: builtin/commit-tree.c builtin/commit.c builtin/merge.c builtin/pull.c
-#: builtin/revert.c
+#: builtin/commit-tree.c builtin/commit.c builtin/merge.c
+#: builtin/pull.c builtin/revert.c
 msgid "GPG sign commit"
 msgstr "GPG 提交簽名"
 
@@ -5832,7 +6061,7 @@
 "in the current commit message"
 msgstr "無法選擇一個未被目前提交說明使用的備註字元"
 
-#: builtin/commit.c
+#: builtin/commit.c builtin/commit.c builtin/commit.c
 #, c-format
 msgid "could not lookup commit '%s'"
 msgstr "無法查詢提交「%s」"
@@ -5856,7 +6085,7 @@
 msgid "options '%s' and '%s:%s' cannot be used together"
 msgstr "「%s」和「%s:%s」選項不得同時使用"
 
-#: builtin/commit.c
+#: builtin/commit.c builtin/commit.c
 msgid "could not read SQUASH_MSG"
 msgstr "無法讀取 SQUASH_MSG"
 
@@ -5864,7 +6093,8 @@
 msgid "could not read MERGE_MSG"
 msgstr "無法讀取 MERGE_MSG"
 
-#: builtin/commit.c bundle.c rerere.c sequencer.c
+#: builtin/commit.c builtin/history.c builtin/submodule--helper.c
+#: bundle.c rerere.c rerere.c sequencer.c sequencer.c
 #, c-format
 msgid "could not open '%s'"
 msgstr "無法開啟「%s」"
@@ -5963,7 +6193,7 @@
 
 #: builtin/commit.c builtin/tag.c
 msgid "unable to pass trailers to --trailers"
-msgstr "無法將尾部署名傳遞至 --trailers"
+msgstr "無法將結尾資訊傳遞至 --trailers"
 
 #: builtin/commit.c
 msgid "Error building trees"
@@ -5984,28 +6214,28 @@
 msgid "Invalid ignored mode '%s'"
 msgstr "無效的忽略模式 '%s'"
 
-#: builtin/commit.c
+#: builtin/commit.c builtin/commit.c
 #, c-format
 msgid "Invalid untracked files mode '%s'"
 msgstr "無效的未追蹤檔案參數 '%s'"
 
 #: builtin/commit.c
 msgid "You are in the middle of a merge -- cannot reword."
-msgstr "正在合併中——不能重新輸入。"
+msgstr "正在合併中——不能改寫說明。"
 
 #: builtin/commit.c
 msgid "You are in the middle of a cherry-pick -- cannot reword."
-msgstr "正在揀選中——不能重新輸入。"
+msgstr "正在揀選中——不能改寫說明。"
 
 #: builtin/commit.c
 #, c-format
 msgid "reword option of '%s' and path '%s' cannot be used together"
-msgstr "「%s」的改寫選項和「%s」路徑不得同時使用"
+msgstr "「%s」的改寫說明選項和「%s」路徑不得同時使用"
 
 #: builtin/commit.c
 #, c-format
 msgid "reword option of '%s' and '%s' cannot be used together"
-msgstr "「%s」的改寫選項和「%s」不得同時使用"
+msgstr "「%s」的改寫說明選項和「%s」不得同時使用"
 
 #: builtin/commit.c
 msgid "You have nothing to amend."
@@ -6037,11 +6267,11 @@
 msgid "paths '%s ...' with -a does not make sense"
 msgstr "路徑 '%s ...' 和 -a 選項同時使用沒有意義"
 
-#: builtin/commit.c
+#: builtin/commit.c builtin/commit.c
 msgid "show status concisely"
 msgstr "以簡潔的格式顯示狀態"
 
-#: builtin/commit.c
+#: builtin/commit.c builtin/commit.c
 msgid "show branch information"
 msgstr "顯示分支訊息"
 
@@ -6049,7 +6279,7 @@
 msgid "show stash information"
 msgstr "顯示貯存區訊息"
 
-#: builtin/commit.c
+#: builtin/commit.c builtin/commit.c
 msgid "compute full ahead/behind values"
 msgstr "計算完整的領先/落後值"
 
@@ -6057,19 +6287,20 @@
 msgid "version"
 msgstr "版本"
 
-#: builtin/commit.c builtin/fetch.c builtin/push.c builtin/worktree.c
+#: builtin/commit.c builtin/commit.c builtin/fetch.c
+#: builtin/push.c builtin/worktree.c
 msgid "machine-readable output"
 msgstr "機器可讀的輸出"
 
-#: builtin/commit.c
+#: builtin/commit.c builtin/commit.c
 msgid "show status in long format (default)"
 msgstr "以長格式顯示狀態(預設值)"
 
-#: builtin/commit.c
+#: builtin/commit.c builtin/commit.c
 msgid "terminate entries with NUL"
 msgstr "條目以 NUL 字元結尾"
 
-#: builtin/commit.c
+#: builtin/commit.c builtin/commit.c
 msgid "show untracked files, optional modes: all, normal, no. (Default: all)"
 msgstr "顯示未追蹤的檔案,「模式」的可選參數:all、normal、no。(預設值:all)"
 
@@ -6140,7 +6371,8 @@
 msgid "override date for commit"
 msgstr "提交時覆蓋日期"
 
-#: builtin/commit.c parse-options.h ref-filter.h
+#: builtin/commit.c builtin/commit.c builtin/commit.c
+#: parse-options.h ref-filter.h
 msgid "commit"
 msgstr "提交"
 
@@ -6172,16 +6404,17 @@
 msgid "the commit is authored by me now (used with -C/-c/--amend)"
 msgstr "現在將該提交的作者改為我(和 -C/-c/--amend 參數共用)"
 
-#: builtin/commit.c builtin/interpret-trailers.c builtin/tag.c
+#: builtin/commit.c builtin/interpret-trailers.c builtin/rebase.c
+#: builtin/tag.c
 msgid "trailer"
-msgstr "尾部署名"
+msgstr "結尾資訊"
 
-#: builtin/commit.c builtin/tag.c
+#: builtin/commit.c builtin/rebase.c builtin/tag.c
 msgid "add custom trailer(s)"
-msgstr "加入自訂尾部署名"
+msgstr "加入自訂結尾資訊"
 
-#: builtin/commit.c builtin/log.c builtin/merge.c builtin/pull.c
-#: builtin/revert.c
+#: builtin/commit.c builtin/log.c builtin/merge.c
+#: builtin/pull.c builtin/revert.c
 msgid "add a Signed-off-by trailer"
 msgstr "在結尾加入 Signed-off-by"
 
@@ -6245,7 +6478,7 @@
 msgid "ok to record a change with an empty message"
 msgstr "允許空的提交說明"
 
-#: builtin/commit.c sequencer.c
+#: builtin/commit.c sequencer.c sequencer.c
 msgid "could not parse HEAD commit"
 msgstr "無法解析 HEAD 提交"
 
@@ -6263,7 +6496,7 @@
 msgid "could not read commit message: %s"
 msgstr "無法讀取提交說明:%s"
 
-#: builtin/commit.c
+#: builtin/commit.c builtin/history.c
 #, c-format
 msgid "Aborting commit due to empty commit message.\n"
 msgstr "終止提交因為提交說明為空。\n"
@@ -6287,7 +6520,7 @@
 "版本庫已更新,但無法寫入新的索引檔案。請檢查磁碟是否\n"
 "已滿或磁碟配額已耗盡,然後執行「git restore --staged :/」復原。"
 
-#: builtin/config.c
+#: builtin/config.c builtin/config.c
 msgid "git config list [<file-option>] [<display-option>] [--includes]"
 msgstr "git config list [<檔案選項>] [<顯示選項>] [--includes]"
 
@@ -6309,7 +6542,7 @@
 "git config set [<file-option>] [--type=<type>] [--all] [--value=<pattern>] "
 "[--fixed-value] <name> <value>"
 
-#: builtin/config.c
+#: builtin/config.c builtin/config.c
 msgid ""
 "git config unset [<file-option>] [--all] [--value=<pattern>] [--fixed-value] "
 "<name>"
@@ -6317,15 +6550,15 @@
 "git config unset [<file-option>] [--all] [--value=<pattern>] [--fixed-value] "
 "<name>"
 
-#: builtin/config.c
+#: builtin/config.c builtin/config.c
 msgid "git config rename-section [<file-option>] <old-name> <new-name>"
 msgstr "git config rename-section [<檔案選項>] <舊名稱> <新名稱>"
 
-#: builtin/config.c
+#: builtin/config.c builtin/config.c
 msgid "git config remove-section [<file-option>] <name>"
 msgstr "git config remove-section [<檔案選項>] <名稱>"
 
-#: builtin/config.c
+#: builtin/config.c builtin/config.c
 msgid "git config edit [<file-option>]"
 msgstr "git config edit [<檔案選項>]"
 
@@ -6371,7 +6604,7 @@
 msgid "use per-worktree config file"
 msgstr "使用工作區級別的設定檔案"
 
-#: builtin/config.c builtin/gc.c
+#: builtin/config.c builtin/gc.c builtin/gc.c
 msgid "use given config file"
 msgstr "使用指定的設定檔案"
 
@@ -6488,7 +6721,7 @@
 msgid "unable to parse default color value"
 msgstr "無法解析預設顏色值"
 
-#: builtin/config.c
+#: builtin/config.c builtin/config.c
 msgid "not in a git directory"
 msgstr "不在 git 版本庫中"
 
@@ -6531,7 +6764,7 @@
 msgid "--worktree can only be used inside a git repository"
 msgstr "--worktree 只能在 git 版本庫中使用"
 
-#: builtin/config.c builtin/gc.c
+#: builtin/config.c builtin/gc.c builtin/gc.c
 msgid "$HOME not set"
 msgstr "$HOME 未設定"
 
@@ -6544,20 +6777,21 @@
 "不能和多個工作區一起使用 --worktree,除非啟用 worktreeConfig 設定擴充部分。\n"
 "詳情請閱讀「git help worktree」的「CONFIGURATION FILE」小節"
 
+#: builtin/config.c builtin/config.c builtin/config.c
 #: builtin/config.c
 msgid "Other"
 msgstr "其他"
 
-#: builtin/config.c
+#: builtin/config.c builtin/config.c builtin/config.c
 msgid "respect include directives on lookup"
 msgstr "查詢時引用 include 指令遞迴尋找"
 
-#: builtin/config.c
+#: builtin/config.c builtin/config.c
 #, c-format
 msgid "unable to read config file '%s'"
 msgstr "無法讀取設定檔案 '%s'"
 
-#: builtin/config.c
+#: builtin/config.c builtin/config.c
 msgid "error processing config file(s)"
 msgstr "處理設定檔案發生錯誤"
 
@@ -6573,10 +6807,11 @@
 msgid "interpret the name as a regular expression"
 msgstr "將名稱當作常規表示式解讀"
 
-#: builtin/config.c
+#: builtin/config.c builtin/config.c
 msgid "show config with values matching the pattern"
 msgstr "顯示值符合模式的組態"
 
+#: builtin/config.c builtin/config.c builtin/config.c
 #: builtin/config.c
 msgid "use string equality when comparing values to value pattern"
 msgstr "使用字串相等比較值和模式"
@@ -6589,6 +6824,7 @@
 msgid "show config matching the given URL"
 msgstr "顯示符合提供 URL 的組態"
 
+#: builtin/config.c builtin/config.c builtin/config.c
 #: builtin/config.c
 msgid "value"
 msgstr "取值"
@@ -6597,7 +6833,7 @@
 msgid "use default value when missing entry"
 msgstr "未指定參數時所使用的預設值"
 
-#: builtin/config.c
+#: builtin/config.c builtin/config.c builtin/config.c
 msgid "--fixed-value only applies with 'value-pattern'"
 msgstr "--fixed-value 僅套用至 'value-pattern'"
 
@@ -6609,7 +6845,7 @@
 msgid "--url= cannot be used with --all, --regexp or --value"
 msgstr "--url= 無法和 --all、--regexp、--value 一起使用"
 
-#: builtin/config.c
+#: builtin/config.c builtin/config.c
 msgid "Filter"
 msgstr "過濾器"
 
@@ -6617,7 +6853,7 @@
 msgid "replace multi-valued config option with new value"
 msgstr "以新值取代有多項值的組態選項"
 
-#: builtin/config.c
+#: builtin/config.c builtin/config.c
 msgid "human-readable comment string (# will be prepended as needed)"
 msgstr "人類可讀的備註字串(將按需插入 # 至前方)"
 
@@ -6650,6 +6886,7 @@
 msgid "unset multi-valued config options with matching values"
 msgstr "取消設定其值相符的多值組態選項"
 
+#: builtin/config.c builtin/config.c builtin/config.c
 #: builtin/config.c
 #, c-format
 msgid "no such section: %s"
@@ -6789,10 +7026,11 @@
 msgstr "除錯訊息輸出到標準錯誤"
 
 #: builtin/credential-cache--daemon.c
+#: builtin/credential-cache--daemon.c
 msgid "credential-cache--daemon unavailable; no unix socket support"
 msgstr "credential-cache--daemon 無法使用;缺少 unix socket 支援"
 
-#: builtin/credential-cache.c
+#: builtin/credential-cache.c builtin/credential-cache.c
 msgid "credential-cache unavailable; no unix socket support"
 msgstr "credential-cache 無法使用;缺少 unix socket 支援"
 
@@ -6951,7 +7189,7 @@
 msgid "show abbreviated commit object as fallback"
 msgstr "顯示簡寫的提交號作為後備"
 
-#: builtin/describe.c
+#: builtin/describe.c builtin/describe.c
 msgid "mark"
 msgstr "標記"
 
@@ -6967,10 +7205,10 @@
 msgid "No names found, cannot describe anything."
 msgstr "沒有發現名稱,無法描述任何東西。"
 
-#: builtin/describe.c
+#: builtin/describe.c builtin/describe.c
 #, c-format
 msgid "option '%s' and commit-ishes cannot be used together"
-msgstr "「%s」選項和提交號不得同時使用"
+msgstr "「%s」選項和提交指示元不得同時使用"
 
 #: builtin/diagnose.c
 msgid ""
@@ -7006,12 +7244,6 @@
 msgid "git diff-pairs -z [<diff-options>]"
 msgstr "git diff-pairs -z [<diff-options>]"
 
-#: builtin/diff-pairs.c builtin/log.c builtin/replay.c builtin/shortlog.c
-#: bundle.c
-#, c-format
-msgid "unrecognized argument: %s"
-msgstr "不認識的引數:%s"
-
 #: builtin/diff-pairs.c
 msgid "working without -z is not supported"
 msgstr "不支援在無 -z 的情況下運作"
@@ -7073,7 +7305,8 @@
 msgid "%s...%s: no merge base"
 msgstr "%s...%s: 無合併基底"
 
-#: builtin/diff.c setup.c
+#: builtin/diff.c setup.c setup.c setup.c setup.c
+#: setup.c
 msgid "cannot come back to cwd"
 msgstr "無法返回目前工作目錄"
 
@@ -7137,7 +7370,7 @@
 msgid "working tree file has been left."
 msgstr "工作區檔案被留了下來。"
 
-#: builtin/difftool.c sequencer.c
+#: builtin/difftool.c sequencer.c sequencer.c sequencer.c
 #, c-format
 msgid "could not copy '%s' to '%s'"
 msgstr "無法將「%s」複製至「%s」"
@@ -7229,7 +7462,7 @@
 msgid "unknown reencoding mode: %s"
 msgstr "重新編碼的模式未知:%s"
 
-#: builtin/fast-export.c
+#: builtin/fast-export.c builtin/fast-export.c
 #, c-format
 msgid "could not read blob %s"
 msgstr "無法讀取資料物件 %s"
@@ -7249,7 +7482,7 @@
 msgid "unexpected comparison status '%c' for %s, %s"
 msgstr "%2$s、%3$s 有非預期的比較狀態「%1$c」"
 
-#: builtin/fast-export.c
+#: builtin/fast-export.c builtin/fast-export.c
 msgid "none"
 msgstr "無"
 
@@ -7287,14 +7520,6 @@
 msgstr "發現已簽署的提交 %s;使用 --signed-commits=<mode> 來處理"
 
 #: builtin/fast-export.c
-msgid ""
-"'strip-if-invalid' is not a valid mode for git fast-export with --signed-"
-"commits=<mode>"
-msgstr ""
-"「strip-if-invalid」不是搭配 --signed-commits=<mode> 使用 git fast-export 的"
-"有效模式"
-
-#: builtin/fast-export.c
 #, c-format
 msgid ""
 "omitting tag %s,\n"
@@ -7324,14 +7549,6 @@
 msgstr "發現已簽署的標籤 %s;使用 --signed-tags=<mode> 來處理"
 
 #: builtin/fast-export.c
-msgid ""
-"'strip-if-invalid' is not a valid mode for git fast-export with --signed-"
-"tags=<mode>"
-msgstr ""
-"「strip-if-invalid」不是搭配 --signed-tags=<mode> 使用 git fast-export 的有效"
-"模式"
-
-#: builtin/fast-export.c
 #, c-format
 msgid ""
 "tag %s tags unexported object; use --tag-of-filtered-object=<mode> to handle "
@@ -7367,12 +7584,14 @@
 msgid "unable to write marks file %s."
 msgstr "無法寫入標記檔案 %s。"
 
-#: builtin/fast-export.c builtin/fast-import.c
+#: builtin/fast-export.c builtin/fast-export.c
+#: builtin/fast-import.c builtin/fast-import.c
 #, c-format
 msgid "corrupt mark line: %s"
 msgstr "損壞的標記列:%s"
 
-#: builtin/fast-export.c builtin/fast-import.c fetch-pack.c
+#: builtin/fast-export.c builtin/fast-import.c
+#: builtin/fast-import.c fetch-pack.c fetch-pack.c
 #, c-format
 msgid "object not found: %s"
 msgstr "物件未找到:%s"
@@ -7545,7 +7764,7 @@
 msgid "not a tree: %s"
 msgstr "非樹狀物件:%s"
 
-#: builtin/fast-import.c
+#: builtin/fast-import.c builtin/fast-import.c
 #, c-format
 msgid "can't load tree %s"
 msgstr "無法載入樹狀物件 %s"
@@ -7559,7 +7778,7 @@
 msgid "root cannot be a non-directory"
 msgstr "根不能不是目錄"
 
-#: builtin/fast-import.c
+#: builtin/fast-import.c builtin/fast-import.c
 msgid "empty path component found in input"
 msgstr "輸入中有空的路徑部分"
 
@@ -7582,8 +7801,8 @@
 msgid "not updating %s (new tip %s does not contain %s)"
 msgstr "沒有更新 %s(提示:%s 不包含 %s)"
 
-#: builtin/fast-import.c builtin/sparse-checkout.c commit-graph.c midx-write.c
-#: sequencer.c
+#: builtin/fast-import.c builtin/sparse-checkout.c commit-graph.c
+#: midx-write.c sequencer.c
 #, c-format
 msgid "unable to create leading directories of %s"
 msgstr "不能為 %s 建立先導目錄"
@@ -7642,7 +7861,7 @@
 msgid "missing space after > in ident string: %s"
 msgstr "身分字串的 > 後面缺少空格:%s"
 
-#: builtin/fast-import.c
+#: builtin/fast-import.c builtin/fast-import.c
 #, c-format
 msgid "invalid raw date \"%s\" in ident: %s"
 msgstr "身分中有無效的原始日期「%s」:%s"
@@ -7702,17 +7921,18 @@
 msgid "missing space after %s: %s"
 msgstr "%s 後缺少空格:%s"
 
-#: builtin/fast-import.c
+#: builtin/fast-import.c builtin/fast-import.c
 #, c-format
 msgid "corrupt mode: %s"
 msgstr "損壞的模式:%s"
 
-#: builtin/fast-import.c
+#: builtin/fast-import.c builtin/fast-import.c
+#: builtin/fast-import.c builtin/fast-import.c
 #, c-format
 msgid "invalid dataref: %s"
 msgstr "無效的資料引用:%s"
 
-#: builtin/fast-import.c
+#: builtin/fast-import.c builtin/fast-import.c
 #, c-format
 msgid "missing space after SHA1: %s"
 msgstr "SHA1 值後缺少空格:%s"
@@ -7755,22 +7975,25 @@
 msgid "can't add a note on empty branch."
 msgstr "不能在空分支上附加註記。"
 
+#: builtin/fast-import.c builtin/fast-import.c
 #: builtin/fast-import.c
 #, c-format
 msgid "mark :%<PRIuMAX> not a commit"
 msgstr "標記 :%<PRIuMAX> 不是提交"
 
+#: builtin/fast-import.c builtin/fast-import.c
 #: builtin/fast-import.c
 #, c-format
 msgid "not a valid commit: %s"
 msgstr "不是有效的提交:%s"
 
-#: builtin/fast-import.c
+#: builtin/fast-import.c builtin/fast-import.c
+#: builtin/fast-import.c builtin/fast-import.c
 #, c-format
 msgid "invalid ref name or SHA1 expression: %s"
 msgstr "無效的引用名稱或 SHA1 表示式:%s"
 
-#: builtin/fast-import.c
+#: builtin/fast-import.c builtin/fast-import.c
 #, c-format
 msgid "not a blob (actually a %s): %s"
 msgstr "不是資料物件(而是 %s):%s"
@@ -7822,7 +8045,8 @@
 msgid "parse_one_signature() returned unknown hash algo"
 msgstr "parse_one_signature() 回傳了未知的雜湊演算法"
 
-#: builtin/fast-import.c builtin/fsck.c
+#: builtin/fast-import.c builtin/fsck.c builtin/fsck.c
+#: builtin/fsck.c
 msgid "unknown"
 msgstr "未知"
 
@@ -7854,6 +8078,45 @@
 "  據稱由 %s 所簽"
 
 #: builtin/fast-import.c
+#, c-format
+msgid ""
+"replacing invalid signature for commit '%.100s...'\n"
+"  allegedly by %s"
+msgstr ""
+"正在取代提交「%.100s...」的無效簽章\n"
+"  據稱由 %s 所簽"
+
+#: builtin/fast-import.c
+#, c-format
+msgid ""
+"replacing invalid signature for commit '%.*s'\n"
+"  allegedly by %s"
+msgstr ""
+"正在取代提交「%.*s」的無效簽章\n"
+"  據稱由 %s 所簽"
+
+#: builtin/fast-import.c
+#, c-format
+msgid ""
+"replacing invalid signature for commit\n"
+"  allegedly by %s"
+msgstr ""
+"正在取代提交的無效簽章\n"
+"  據稱由 %s 所簽"
+
+#: builtin/fast-import.c builtin/fast-import.c
+msgid "aborting due to invalid signature"
+msgstr "因為簽章無效而中止"
+
+#: builtin/fast-import.c
+msgid "signing commits in interoperability mode is unsupported"
+msgstr "不支援在互通模式下簽署提交"
+
+#: builtin/fast-import.c
+msgid "failed to sign commit object"
+msgstr "無法簽署提交物件"
+
+#: builtin/fast-import.c
 msgid "expected committer but didn't get one"
 msgstr "預期有提交者,卻沒收到"
 
@@ -7870,6 +8133,10 @@
 msgstr "按原樣匯入提交簽章"
 
 #: builtin/fast-import.c
+msgid "failed to sign tag object"
+msgstr "無法簽署標籤物件"
+
+#: builtin/fast-import.c
 #, c-format
 msgid "importing a tag signature verbatim for tag '%s'"
 msgstr "按原樣匯入標籤「%s」的簽章"
@@ -7884,14 +8151,6 @@
 msgstr "發現已簽署的標籤;使用 --signed-tags=<mode> 來處理"
 
 #: builtin/fast-import.c
-msgid ""
-"'strip-if-invalid' is not a valid mode for git fast-import with --signed-"
-"tags=<mode>"
-msgstr ""
-"「strip-if-invalid」不是搭配 --signed-tags=<mode> 使用 git fast-import 的有效"
-"模式"
-
-#: builtin/fast-import.c
 #, c-format
 msgid "expected 'from' command, got '%s'"
 msgstr "預期「from」命令,卻收到「%s」"
@@ -7924,6 +8183,7 @@
 msgid "not a mark: %s"
 msgstr "不是標記:%s"
 
+#: builtin/fast-import.c builtin/fast-import.c
 #: builtin/fast-import.c
 #, c-format
 msgid "unknown mark: %s"
@@ -8118,7 +8378,7 @@
 msgid "[up to date]"
 msgstr "[最新]"
 
-#: builtin/fetch.c
+#: builtin/fetch.c builtin/fetch.c builtin/fetch.c
 msgid "[rejected]"
 msgstr "[已拒絕]"
 
@@ -8130,6 +8390,7 @@
 msgid "[tag update]"
 msgstr "[標籤更新]"
 
+#: builtin/fetch.c builtin/fetch.c builtin/fetch.c
 #: builtin/fetch.c
 msgid "unable to update local ref"
 msgstr "不能更新本機引用"
@@ -8158,7 +8419,8 @@
 msgid "non-fast-forward"
 msgstr "非快轉"
 
-#: builtin/fetch.c builtin/grep.c sequencer.c
+#: builtin/fetch.c builtin/fetch.c builtin/grep.c
+#: sequencer.c
 #, c-format
 msgid "cannot open '%s'"
 msgstr "不能開啟 '%s'"
@@ -8314,12 +8576,12 @@
 "找不到來源分支。\n"
 "您得使用 --set-upstream 選項明確指定一個分支"
 
-#: builtin/fetch.c
+#: builtin/fetch.c builtin/fetch.c
 #, c-format
 msgid "Fetching %s\n"
 msgstr "正在取得 %s\n"
 
-#: builtin/fetch.c
+#: builtin/fetch.c builtin/fetch.c
 #, c-format
 msgid "could not fetch %s"
 msgstr "無法取得 %s"
@@ -8394,7 +8656,7 @@
 msgstr "清除遠端不存在的本機標籤,並且取代變更標籤"
 
 #  譯者:可選值,不能翻譯
-#: builtin/fetch.c builtin/pull.c
+#: builtin/fetch.c builtin/fetch.c builtin/pull.c
 msgid "on-demand"
 msgstr "on-demand"
 
@@ -8414,7 +8676,8 @@
 msgid "allow updating of HEAD ref"
 msgstr "允許更新 HEAD 引用"
 
-#: builtin/fetch.c builtin/pull.c
+#: builtin/fetch.c builtin/fetch.c builtin/pull.c
+#: builtin/pull.c
 msgid "deepen history of shallow clone"
 msgstr "取得淺拓製的更多過去歷史記錄"
 
@@ -8452,7 +8715,8 @@
 msgid "specify fetch refmap"
 msgstr "指定取得動作的引用映射"
 
-#: builtin/fetch.c builtin/pull.c builtin/rebase.c builtin/replay.c
+#: builtin/fetch.c builtin/pull.c builtin/rebase.c
+#: builtin/replay.c
 msgid "revision"
 msgstr "revision"
 
@@ -8464,7 +8728,7 @@
 msgid "do not fetch a packfile; instead, print ancestors of negotiation tips"
 msgstr "不取得 packfile,而是輸出協商的祖先提交"
 
-#: builtin/fetch.c
+#: builtin/fetch.c builtin/fetch.c
 msgid "run 'maintenance --auto' after fetching"
 msgstr "取得 (fetch) 後執行 'maintenance --auto'"
 
@@ -8667,7 +8931,7 @@
 msgstr "收到無效的組態 --config=%s"
 
 #. TRANSLATORS: e.g. error in tree 01bfda: <more explanation>
-#: builtin/fsck.c
+#: builtin/fsck.c builtin/fsck.c
 #, c-format
 msgid "error in %s %s: %s"
 msgstr "%s %s 錯誤:%s"
@@ -8678,7 +8942,7 @@
 msgid "warning in %s %s: %s"
 msgstr "%s %s 警告:%s"
 
-#: builtin/fsck.c
+#: builtin/fsck.c builtin/fsck.c
 #, c-format
 msgid "broken link from %7s %s"
 msgstr "來自 %7s %s 的損壞的連結"
@@ -8719,7 +8983,8 @@
 msgid "could not create lost-found"
 msgstr "無法建立 lost-found"
 
-#: builtin/fsck.c builtin/gc.c builtin/rebase.c rebase-interactive.c rerere.c
+#: builtin/fsck.c builtin/gc.c builtin/rebase.c
+#: rebase-interactive.c rerere.c rerere.c sequencer.c
 #: sequencer.c
 #, c-format
 msgid "could not write '%s'"
@@ -8850,7 +9115,7 @@
 msgid "invalid rev-index for pack '%s'"
 msgstr "「%s」封裝的修訂版索引 (rev-index) 無效"
 
-#: builtin/fsck.c
+#: builtin/fsck.c builtin/fsck.c
 msgid "Checking ref database"
 msgstr "正在檢查引用資料庫"
 
@@ -8930,7 +9195,7 @@
 msgid "git fsmonitor--daemon run [<options>]"
 msgstr "git fsmonitor--daemon run [<options>]"
 
-#: builtin/fsmonitor--daemon.c
+#: builtin/fsmonitor--daemon.c builtin/fsmonitor--daemon.c
 #, c-format
 msgid "value of '%s' out of range: %d"
 msgstr "「%s」的數值超出範圍:%d"
@@ -8986,7 +9251,7 @@
 msgid "could not cd home '%s'"
 msgstr "無法 cd home '%s'"
 
-#: builtin/fsmonitor--daemon.c
+#: builtin/fsmonitor--daemon.c builtin/fsmonitor--daemon.c
 #, c-format
 msgid "fsmonitor--daemon is already running '%s'"
 msgstr "fsmonitor--daemon 已在執行「%s」"
@@ -9225,7 +9490,7 @@
 msgid "task '%s' cannot be selected multiple times"
 msgstr "不能多次選取 '%s' 作業"
 
-#: builtin/gc.c
+#: builtin/gc.c builtin/gc.c
 msgid "run tasks based on the state of the repository"
 msgstr "基於版本庫狀態執行作業"
 
@@ -9245,7 +9510,7 @@
 msgid "do not report progress or other information over stderr"
 msgstr "不要在 stderr 輸出進度或其他資訊"
 
-#: builtin/gc.c
+#: builtin/gc.c builtin/gc.c
 msgid "task"
 msgstr "作業"
 
@@ -9277,11 +9542,11 @@
 msgid "failed to expand path '%s'"
 msgstr "無法展開「%s」路徑"
 
-#: builtin/gc.c
+#: builtin/gc.c builtin/gc.c
 msgid "failed to start launchctl"
 msgstr "無法啟動 launchctl"
 
-#: builtin/gc.c
+#: builtin/gc.c builtin/gc.c builtin/gc.c
 #, c-format
 msgid "failed to create directories for '%s'"
 msgstr "無法建立「%s」的目錄"
@@ -9319,12 +9584,13 @@
 msgid "'crontab' died"
 msgstr "「crontab」結束運作"
 
-#: builtin/gc.c builtin/worktree.c
+#: builtin/gc.c builtin/gc.c builtin/gc.c builtin/worktree.c
+#: builtin/worktree.c
 #, c-format
 msgid "failed to delete '%s'"
 msgstr "刪除 '%s' 失敗"
 
-#: builtin/gc.c rerere.c
+#: builtin/gc.c builtin/gc.c rerere.c
 #, c-format
 msgid "failed to flush '%s'"
 msgstr "排清 '%s' 失敗"
@@ -9415,11 +9681,13 @@
 msgid "invalid number of threads specified (%d) for %s"
 msgstr "為 %2$s 設定的執行緒數 (%1$d) 無效"
 
+#. #-#-#-#-#  grep.c.po  #-#-#-#-#
 #. TRANSLATORS: %s is the configuration
 #. variable for tweaking threads, currently
 #. grep.threads
 #.
-#: builtin/grep.c builtin/index-pack.c builtin/pack-objects.c
+#: builtin/grep.c builtin/index-pack.c builtin/index-pack.c
+#: builtin/pack-objects.c
 #, c-format
 msgid "no threads support, ignoring %s"
 msgstr "沒有執行緒支援,忽略 %s"
@@ -9777,7 +10045,7 @@
 msgid "emacsclient version '%d' too old (< 22)."
 msgstr "emacsclient 版本 '%d' 太老(< 22)。"
 
-#: builtin/help.c
+#: builtin/help.c builtin/help.c builtin/help.c builtin/help.c
 #, c-format
 msgid "failed to exec '%s'"
 msgstr "執行 '%s' 失敗"
@@ -9813,7 +10081,7 @@
 msgid "no info viewer handled the request"
 msgstr "沒有 info 檢視器處理此請求"
 
-#: builtin/help.c git.c
+#: builtin/help.c builtin/help.c git.c
 #, c-format
 msgid "'%s' is aliased to '%s'"
 msgstr "'%s' 是 '%s' 的別名"
@@ -9833,7 +10101,7 @@
 "the '--no-[external-commands|aliases]' options can only be used with '--all'"
 msgstr "「--no-[external-commands|aliases]」選項只能與「--all」一起使用"
 
-#: builtin/help.c
+#: builtin/help.c builtin/help.c
 #, c-format
 msgid "usage: %s%s"
 msgstr "用法:%s%s"
@@ -9842,13 +10110,195 @@
 msgid "'git help config' for more information"
 msgstr "'git help config' 取得更多訊息"
 
+#: builtin/history.c
+msgid "git history reword <commit> [--dry-run] [--update-refs=(branches|head)]"
+msgstr ""
+"git history reword <commit> [--dry-run] [--update-refs=(branches|head)]"
+
+#: builtin/history.c
+msgid ""
+"git history split <commit> [--dry-run] [--update-refs=(branches|head)] [--] "
+"[<pathspec>...]"
+msgstr ""
+"git history split <commit> [--dry-run] [--update-refs=(branches|head)] [--] "
+"[<pathspec>...]"
+
+#: builtin/history.c
+#, c-format
+msgid ""
+"Please enter the commit message for the %s changes. Lines starting\n"
+"with '%s' will be ignored, and an empty message aborts the commit.\n"
+msgstr ""
+"請輸入描述 %s 變更的提交訊息。開頭是「%s」的列\n"
+"皆會忽略。提交訊息空白則取消本次提交作業。\n"
+
+#: builtin/history.c
+#, c-format
+msgid "Aborting commit as launching the editor failed.\n"
+msgstr "中止提交,因為無法開啟編輯器。\n"
+
+#: builtin/history.c builtin/history.c
+#, c-format
+msgid "unable to parse parent commit %s"
+msgstr "無法解析上級提交 %s"
+
+#: builtin/history.c
+#, c-format
+msgid "%s expects one of 'branches' or 'head'"
+msgstr "%s 預期是「branches」或「head」之一"
+
+#: builtin/history.c replay.c
+msgid "error preparing revisions"
+msgstr "無法準備修訂集"
+
+#: builtin/history.c replay.c
+msgid "replaying merge commits is not supported yet!"
+msgstr "尚不支援重放合併提交!"
+
+#: builtin/history.c
+msgid "cannot look up HEAD"
+msgstr "無法查詢 HEAD"
+
+#: builtin/history.c
+msgid "cannot determine descendance"
+msgstr "無法判定繼承關係"
+
+#: builtin/history.c
+msgid ""
+"rewritten commit must be an ancestor of HEAD when using --update-refs=head"
+msgstr "使用 --update-refs=head 時,重寫後的提交必須是 HEAD 的祖先"
+
+#: builtin/history.c builtin/replay.c
+#, c-format
+msgid "failed to begin ref transaction: %s"
+msgstr "無法開始引用事務:%s"
+
+#: builtin/history.c builtin/history.c builtin/replay.c
+#, c-format
+msgid "failed to update ref '%s': %s"
+msgstr "無法更新「%s」引用:%s"
+
+#: builtin/history.c builtin/replay.c
+#, c-format
+msgid "failed to commit ref transaction: %s"
+msgstr "無法提交引用事務:%s"
+
+#: builtin/history.c
+msgid "control which refs should be updated"
+msgstr "控制要更新的引用"
+
+#: builtin/history.c builtin/history.c
+msgid "perform a dry-run without updating any refs"
+msgstr "進行測試執行,不更新任何引用"
+
+#: builtin/history.c
+msgid "command expects a single revision"
+msgstr "命令預期只有一個修訂版"
+
+#: builtin/history.c builtin/history.c
+#, c-format
+msgid "commit cannot be found: %s"
+msgstr "找不到提交:%s"
+
+#: builtin/history.c
+msgid "failed writing reworded commit"
+msgstr "無法寫入改寫說明後的提交"
+
+#: builtin/history.c builtin/history.c
+msgid "failed replaying descendants"
+msgstr "重放後代失敗"
+
+#: builtin/history.c
+msgid "unable to populate index with tree"
+msgstr "無法以樹狀物件填入索引"
+
+#: builtin/history.c
+msgid "unable to acquire index lock"
+msgstr "無法取得索引鎖"
+
+#: builtin/history.c
+msgid "failed reading temporary index"
+msgstr "無法讀取暫存索引"
+
+#: builtin/history.c
+msgid "failed split tree"
+msgstr "無法拆分樹狀物件"
+
+#: builtin/history.c
+msgid "split commit is empty"
+msgstr "拆出的提交是空白的"
+
+#: builtin/history.c
+msgid "split commit tree matches original commit"
+msgstr "拆分提交的樹狀物件和原始提交相同"
+
+#: builtin/history.c
+msgid "failed writing first commit"
+msgstr "無法寫入第一個提交"
+
+#: builtin/history.c
+msgid "failed writing second commit"
+msgstr "無法寫入第二個提交"
+
+#: builtin/history.c
+msgid "control ref update behavior"
+msgstr "控制引用的更新行為"
+
+#: builtin/history.c
+msgid "command expects a committish"
+msgstr "命令預期有一個提交指示元"
+
+#: builtin/history.c
+msgid "cannot split up merge commit"
+msgstr "無法拆分合併提交"
+
 #: builtin/hook.c
 msgid ""
-"git hook run [--ignore-missing] [--to-stdin=<path>] <hook-name> [-- <hook-"
-"args>]"
+"git hook run [--allow-unknown-hook-name] [--ignore-missing] [--to-"
+"stdin=<path>] <hook-name> [-- <hook-args>]"
 msgstr ""
-"git hook run [--ignore-missing] [--to-stdin=<path>] <hook-name> [-- <hook-"
-"args>]"
+"git hook run [--allow-unknown-hook-name] [--ignore-missing] [--to-"
+"stdin=<path>] <hook-name> [-- <hook-args>]"
+
+#: builtin/hook.c
+msgid ""
+"git hook list [--allow-unknown-hook-name] [-z] [--show-scope] <hook-name>"
+msgstr ""
+"git hook list [--allow-unknown-hook-name] [-z] [--show-scope] <hook-name>"
+
+#: builtin/hook.c
+msgid "use NUL as line terminator"
+msgstr "使用 NUL 作為列終止符"
+
+#: builtin/hook.c
+msgid "show the config scope that defined each hook"
+msgstr "顯示定義每個掛鉤的組態範圍"
+
+#: builtin/hook.c builtin/hook.c
+msgid "allow running a hook with a non-native hook name"
+msgstr "允許執行非原生名稱的掛鉤"
+
+#: builtin/hook.c
+msgid "you must specify a hook event name to list"
+msgstr "您必須指定要列出的掛鉤事件名稱"
+
+#: builtin/hook.c builtin/hook.c
+#, c-format
+msgid ""
+"unknown hook event '%s';\n"
+"use --allow-unknown-hook-name to allow non-native hook names"
+msgstr ""
+"未知的掛鉤事件「%s」,\n"
+"請使用 --allow-unknown-hook-name 以允許指定非原生的掛鉤名稱"
+
+#: builtin/hook.c
+#, c-format
+msgid "no hooks found for event '%s'"
+msgstr "找不到事件「%s」的掛鉤"
+
+#: builtin/hook.c
+msgid "hook from hookdir"
+msgstr "來自 hookdir 的掛鉤"
 
 #: builtin/hook.c
 msgid "silently ignore missing requested <hook-name>"
@@ -9946,7 +10396,8 @@
 msgid "serious inflate inconsistency"
 msgstr "解壓縮嚴重的不一致"
 
-#: builtin/index-pack.c
+#: builtin/index-pack.c builtin/index-pack.c builtin/index-pack.c
+#: builtin/index-pack.c builtin/index-pack.c
 #, c-format
 msgid "SHA1 COLLISION FOUND WITH %s !"
 msgstr "發現 %s 出現 SHA1 衝突!"
@@ -9966,7 +10417,7 @@
 msgid "invalid blob object %s"
 msgstr "無效的資料物件 %s"
 
-#: builtin/index-pack.c
+#: builtin/index-pack.c builtin/index-pack.c
 msgid "fsck error in packed object"
 msgstr "對打包物件 fsck 檢查發生錯誤"
 
@@ -9980,7 +10431,7 @@
 msgid "Not all child objects of %s are reachable"
 msgstr "%s 的所有子物件並非都可以取得"
 
-#: builtin/index-pack.c
+#: builtin/index-pack.c builtin/index-pack.c
 msgid "failed to apply delta"
 msgstr "套用 delta 失敗"
 
@@ -10013,6 +10464,7 @@
 msgstr "處理 delta 中"
 
 #: builtin/index-pack.c builtin/pack-objects.c
+#: builtin/pack-objects.c
 #, c-format
 msgid "unable to create thread: %s"
 msgstr "不能建立執行緒:%s"
@@ -10030,7 +10482,7 @@
 #: builtin/index-pack.c
 #, c-format
 msgid "Unexpected tail checksum for %s (disk corruption?)"
-msgstr "對 %s 的尾部總和檢查出現意外(磁碟損壞?)"
+msgstr "%s 的尾部總和檢查碼與預期不符(磁碟損壞?)"
 
 #: builtin/index-pack.c
 #, c-format
@@ -10124,12 +10576,12 @@
 msgid "bad --pack_header: %s"
 msgstr "無效的 --pack_header:%s"
 
-#: builtin/index-pack.c
+#: builtin/index-pack.c builtin/index-pack.c
 #, c-format
 msgid "bad %s"
 msgstr "錯誤選項 %s"
 
-#: builtin/index-pack.c builtin/init-db.c setup.c
+#: builtin/index-pack.c builtin/init-db.c setup.c setup.c
 #, c-format
 msgid "unknown hash algorithm '%s'"
 msgstr "未知的「%s」雜湊算法"
@@ -10188,12 +10640,12 @@
 msgid "specify the hash algorithm to use"
 msgstr "指定要使用的雜湊算法"
 
-#: builtin/init-db.c
+#: builtin/init-db.c builtin/init-db.c
 #, c-format
 msgid "cannot mkdir %s"
 msgstr "不能建立目錄 %s"
 
-#: builtin/init-db.c
+#: builtin/init-db.c builtin/init-db.c
 #, c-format
 msgid "cannot chdir to %s"
 msgstr "不能切換目錄到 %s"
@@ -10224,25 +10676,6 @@
 "                       [(--trailer (<key>|<key-alias>)[(=|:)<value>])...]\n"
 "                       [--parse] [<file>...]"
 
-#: builtin/interpret-trailers.c wrapper.c
-#, c-format
-msgid "could not stat %s"
-msgstr "無法 stat %s"
-
-#: builtin/interpret-trailers.c
-#, c-format
-msgid "file %s is not a regular file"
-msgstr "檔案 %s 不是一個正規檔案"
-
-#: builtin/interpret-trailers.c
-#, c-format
-msgid "file %s is not writable by user"
-msgstr "檔案 %s 使用者不可寫"
-
-#: builtin/interpret-trailers.c
-msgid "could not open temporary file"
-msgstr "無法開啟暫存檔"
-
 #: builtin/interpret-trailers.c
 #, c-format
 msgid "could not read input file '%s'"
@@ -10254,6 +10687,11 @@
 
 #: builtin/interpret-trailers.c
 #, c-format
+msgid "could not write to temporary file '%s'"
+msgstr "無法寫入暫存檔案「%s」"
+
+#: builtin/interpret-trailers.c trailer.c
+#, c-format
 msgid "could not rename temporary file to %s"
 msgstr "無法將暫存檔重新命名為 %s"
 
@@ -10263,7 +10701,7 @@
 
 #: builtin/interpret-trailers.c
 msgid "trim empty trailers"
-msgstr "刪除空的尾部署名"
+msgstr "刪除空的結尾資訊"
 
 #: builtin/interpret-trailers.c
 msgid "placement"
@@ -10271,27 +10709,27 @@
 
 #: builtin/interpret-trailers.c
 msgid "where to place the new trailer"
-msgstr "在哪裡放置新的尾部署名"
+msgstr "放置新結尾資訊的位置"
 
 #: builtin/interpret-trailers.c
 msgid "action if trailer already exists"
-msgstr "當尾部署名已經存在時所採取的動作"
+msgstr "當結尾資訊已經存在時應採取的動作"
 
 #: builtin/interpret-trailers.c
 msgid "action if trailer is missing"
-msgstr "當尾部署名缺少時所採取的動作"
+msgstr "當結尾資訊缺少時應採取的動作"
 
 #: builtin/interpret-trailers.c
 msgid "output only the trailers"
-msgstr "只輸出尾部署名"
+msgstr "只輸出結尾資訊"
 
 #: builtin/interpret-trailers.c
-msgid "do not apply trailer.* configuration variables"
-msgstr "不套用 trailer.* 組態變數"
+msgid "do not apply trailer.<key-alias> configuration variables"
+msgstr "不要套用 trailer.<key-alias> 組態變數"
 
 #: builtin/interpret-trailers.c
 msgid "reformat multiline trailer values as single-line values"
-msgstr "將多列尾注值 (trailer values) 重新格式化為單列值"
+msgstr "將多列結尾資訊值重新格式化為單列值"
 
 #: builtin/interpret-trailers.c
 msgid "alias for --only-trailers --only-input --unfold"
@@ -10303,7 +10741,7 @@
 
 #: builtin/interpret-trailers.c
 msgid "trailer(s) to add"
-msgstr "要新增的尾部署名"
+msgstr "要加入的結尾資訊"
 
 #: builtin/interpret-trailers.c
 msgid "--trailer with --only-input does not make sense"
@@ -10314,8 +10752,13 @@
 msgstr "沒有給出要原位編輯的檔案"
 
 #: builtin/last-modified.c
-msgid "last-modified can only operate on one tree at a time"
-msgstr "last-modified 一次只能操作一個樹狀物件"
+msgid "last-modified can only operate on one commit at a time"
+msgstr "last-modified 一次只能操作一個提交"
+
+#: builtin/last-modified.c
+#, c-format
+msgid "revision argument '%s' is a %s, not a commit-ish"
+msgstr "修訂版本引數「%s」是 %s,而非提交指示元"
 
 #: builtin/last-modified.c
 #, c-format
@@ -10323,16 +10766,12 @@
 msgstr "未知的 last-modified 引數:%s"
 
 #: builtin/last-modified.c
-msgid "unable to setup last-modified"
-msgstr "無法設定 last-modified"
-
-#: builtin/last-modified.c
 msgid ""
-"git last-modified [--recursive] [--show-trees] [<revision-range>] [[--] "
-"<path>...]"
+"git last-modified [--recursive] [--show-trees] [--max-depth=<depth>] [-z]\n"
+"                  [<revision-range>] [[--] <pathspec>...]"
 msgstr ""
-"git last-modified [--recursive] [--show-trees] [<revision-range>] [[--] "
-"<path>...]"
+"git last-modified [--recursive] [--show-trees] [--max-depth=<depth>] [-z]\n"
+"                  [<revision-range>] [[--] <pathspec>...]"
 
 #: builtin/last-modified.c builtin/ls-tree.c
 msgid "recurse into subtrees"
@@ -10342,6 +10781,14 @@
 msgid "show tree entries when recursing into subtrees"
 msgstr "遞迴子樹時顯示樹狀物件項目"
 
+#: builtin/last-modified.c diff.c
+msgid "maximum tree depth to recurse"
+msgstr "樹狀物件的遞迴最大深度"
+
+#: builtin/last-modified.c
+msgid "lines are separated with NUL character"
+msgstr "列以 NUL 字元分隔"
+
 #: builtin/log.c
 msgid "git log [<options>] [<revision-range>] [[--] <path>...]"
 msgstr "git log [<選項>] [<版本範圍>] [[--] <路徑>...]"
@@ -10410,7 +10857,7 @@
 msgid "git show %s: bad file"
 msgstr "git show %s: 損壞的檔案"
 
-#: builtin/log.c
+#: builtin/log.c builtin/log.c
 #, c-format
 msgid "could not read object %s"
 msgstr "無法讀取物件 %s"
@@ -10429,6 +10876,20 @@
 msgid "format.headers without value"
 msgstr "format.headers 沒有值"
 
+#: builtin/log.c config.c
+#, c-format
+msgid "bad boolean config value '%s' for '%s'"
+msgstr "「%2$s」的「%1$s」布林設定值無效"
+
+#: builtin/log.c
+#, c-format
+msgid ""
+"'%s' used to accept any value and treat that as 'true'.\n"
+"Now it only accepts boolean values, like what '%s' does.\n"
+msgstr ""
+"「%s」過去接受任何值,並將其視為「true」。\n"
+"現在它只接受布林值,符合「%s」的行為。\n"
+
 #: builtin/log.c
 #, c-format
 msgid "cannot open patch file %s"
@@ -10457,6 +10918,11 @@
 
 #: builtin/log.c
 #, c-format
+msgid "'%s' is not a valid format string"
+msgstr "「%s」不是有效的格式字串"
+
+#: builtin/log.c
+#, c-format
 msgid "insane in-reply-to: %s"
 msgstr "不正常的 in-reply-to:%s"
 
@@ -10468,12 +10934,13 @@
 msgid "two output directories?"
 msgstr "兩個輸出目錄?"
 
-#: builtin/log.c
+#: builtin/log.c builtin/log.c builtin/log.c builtin/log.c
 #, c-format
 msgid "unknown commit %s"
 msgstr "未知提交 %s"
 
-#: builtin/log.c builtin/replace.c
+#: builtin/log.c builtin/replace.c builtin/replace.c
+#: builtin/replace.c
 #, c-format
 msgid "failed to resolve '%s' as a valid ref"
 msgstr "無法將 '%s' 解析為一個有效引用"
@@ -10534,6 +11001,14 @@
 msgstr "生成一封附函"
 
 #: builtin/log.c
+msgid "format-spec"
+msgstr "format-spec"
+
+#: builtin/log.c
+msgid "format spec used for the commit list in the cover letter"
+msgstr "用於附函中提交列表的格式規格"
+
+#: builtin/log.c
 msgid "use simple number sequence for output file names"
 msgstr "使用簡單的數字序列作為輸出檔案名"
 
@@ -10621,7 +11096,7 @@
 msgid "add email header"
 msgstr "新增信件頭"
 
-#: builtin/log.c
+#: builtin/log.c builtin/log.c
 msgid "email"
 msgstr "信箱"
 
@@ -10649,7 +11124,7 @@
 msgid "make first mail a reply to <message-id>"
 msgstr "使第一封信件作為對 <信件標記> 的回覆"
 
-#: builtin/log.c
+#: builtin/log.c builtin/log.c
 msgid "boundary"
 msgstr "邊界"
 
@@ -10730,7 +11205,8 @@
 msgid "--remerge-diff does not make sense"
 msgstr "--remerge-diff 無意義"
 
-#: builtin/log.c builtin/submodule--helper.c rerere.c submodule.c
+#: builtin/log.c builtin/submodule--helper.c
+#: builtin/submodule--helper.c rerere.c submodule.c
 #, c-format
 msgid "could not create directory '%s'"
 msgstr "無法建立「%s」目錄"
@@ -10922,11 +11398,11 @@
 msgid "do not print remote URL"
 msgstr "不列印遠端 URL"
 
-#: builtin/ls-remote.c builtin/rebase.c
+#: builtin/ls-remote.c builtin/ls-remote.c builtin/rebase.c
 msgid "exec"
 msgstr "exec"
 
-#: builtin/ls-remote.c
+#: builtin/ls-remote.c builtin/ls-remote.c
 msgid "path of git-upload-pack on the remote host"
 msgstr "遠端主機上的 git-upload-pack 路徑"
 
@@ -10978,7 +11454,7 @@
 msgid "include object size"
 msgstr "包括物件大小"
 
-#: builtin/ls-tree.c
+#: builtin/ls-tree.c builtin/ls-tree.c
 msgid "list only filenames"
 msgstr "只列出檔案名"
 
@@ -11156,7 +11632,7 @@
 msgid "not handling anything other than two heads merge."
 msgstr "不能處理兩個頭合併之外的任何動作。"
 
-#: builtin/merge-recursive.c
+#: builtin/merge-recursive.c builtin/merge-recursive.c
 #, c-format
 msgid "could not resolve ref '%s'"
 msgstr "無法解析引用「%s」"
@@ -11166,12 +11642,12 @@
 msgid "Merging %s with %s\n"
 msgstr "合併 %s 和 %s\n"
 
-#: builtin/merge-tree.c
+#: builtin/merge-tree.c builtin/merge-tree.c builtin/merge-tree.c
 #, c-format
 msgid "could not parse as tree '%s'"
 msgstr "無法解析為樹狀物件「%s」"
 
-#: builtin/merge-tree.c builtin/merge.c
+#: builtin/merge-tree.c builtin/merge-tree.c builtin/merge.c
 msgid "not something we can merge"
 msgstr "不能合併"
 
@@ -11231,7 +11707,7 @@
 msgid "option for selected merge strategy"
 msgstr "所選的合併策略的選項"
 
-#: builtin/merge-tree.c
+#: builtin/merge-tree.c builtin/merge-tree.c builtin/merge-tree.c
 msgid "--trivial-merge is incompatible with all other options"
 msgstr "--trivial-merge 和其他所有選項都不相容"
 
@@ -11240,7 +11716,7 @@
 msgid "unknown strategy option: -X%s"
 msgstr "未知的策略選項:-X%s"
 
-#: builtin/merge-tree.c builtin/notes.c
+#: builtin/merge-tree.c builtin/merge-tree.c builtin/notes.c
 #, c-format
 msgid "malformed input line: '%s'."
 msgstr "格式錯誤的輸入行:'%s'。"
@@ -11317,8 +11793,8 @@
 msgid "verify that the named commit has a valid GPG signature"
 msgstr "驗證指定的提交是否包含一個有效的 GPG 簽名"
 
-#: builtin/merge.c builtin/notes.c builtin/pull.c builtin/rebase.c
-#: builtin/revert.c
+#: builtin/merge.c builtin/notes.c builtin/pull.c
+#: builtin/rebase.c builtin/revert.c
 msgid "strategy"
 msgstr "策略"
 
@@ -11359,7 +11835,7 @@
 msgid "stash failed"
 msgstr "貯存失敗"
 
-#: builtin/merge.c
+#: builtin/merge.c builtin/merge.c
 msgid "read-tree failed"
 msgstr "讀取樹失敗"
 
@@ -11392,7 +11868,7 @@
 msgid "Bad branch.%s.mergeoptions string: %s"
 msgstr "壞的 branch.%s.mergeoptions 字串:%s"
 
-#: builtin/merge.c merge-ort-wrappers.c
+#: builtin/merge.c builtin/merge.c merge-ort-wrappers.c
 msgid "Unable to write index."
 msgstr "不能寫入索引。"
 
@@ -11567,7 +12043,7 @@
 msgid "Nope.\n"
 msgstr "無。\n"
 
-#: builtin/merge.c
+#: builtin/merge.c builtin/merge.c
 #, c-format
 msgid "Rewinding the tree to pristine...\n"
 msgstr "將樹回滾至原始狀態...\n"
@@ -11648,11 +12124,21 @@
 
 #: builtin/multi-pack-index.c
 msgid ""
-"git multi-pack-index [<options>] write [--preferred-pack=<pack>][--refs-"
-"snapshot=<path>]"
+"git multi-pack-index [<options>] write [--preferred-pack=<pack>]\n"
+"  [--[no-]bitmap] [--[no-]incremental] [--[no-]stdin-packs]\n"
+"  [--refs-snapshot=<path>]"
 msgstr ""
-"git multi-pack-index [<選項>] write [--preferred-pack=<包>] [--refs-"
-"snapshot=<路徑>]"
+"git multi-pack-index [<options>] write [--preferred-pack=<pack>]\n"
+"  [--[no-]bitmap] [--[no-]incremental] [--[no-]stdin-packs]\n"
+"  [--refs-snapshot=<path>]"
+
+#: builtin/multi-pack-index.c
+msgid ""
+"git multi-pack-index [<options>] compact [--[no-]incremental]\n"
+"  [--[no-]bitmap] <from> <to>"
+msgstr ""
+"git multi-pack-index [<options>] compact [--[no-]incremental]\n"
+"  [--[no-]bitmap] <from> <to>"
 
 #: builtin/multi-pack-index.c
 msgid "git multi-pack-index [<options>] verify"
@@ -11682,11 +12168,11 @@
 msgid "pack for reuse when computing a multi-pack bitmap"
 msgstr "計算多包位圖時要重複使用的包"
 
-#: builtin/multi-pack-index.c
+#: builtin/multi-pack-index.c builtin/multi-pack-index.c
 msgid "write multi-pack bitmap"
 msgstr "寫入多包位圖"
 
-#: builtin/multi-pack-index.c
+#: builtin/multi-pack-index.c builtin/multi-pack-index.c
 msgid "write a new incremental MIDX"
 msgstr "寫入新的增量 MIDX"
 
@@ -11698,6 +12184,20 @@
 msgid "refs snapshot for selecting bitmap commits"
 msgstr "用來選取位圖提交的引用快照"
 
+#: builtin/multi-pack-index.c builtin/multi-pack-index.c
+#, c-format
+msgid "could not find MIDX: %s"
+msgstr "找不到 MIDX:%s"
+
+#: builtin/multi-pack-index.c
+msgid "MIDX compaction endpoints must be unique"
+msgstr "MIDX 壓縮端點必須唯一"
+
+#: builtin/multi-pack-index.c
+#, c-format
+msgid "MIDX %s must be an ancestor of %s"
+msgstr "MIDX %s 必須是 %s 的祖先"
+
 #: builtin/multi-pack-index.c
 msgid ""
 "during repack, collect pack-files of smaller size into a batch that is "
@@ -11744,11 +12244,11 @@
 msgid "Checking rename of '%s' to '%s'\n"
 msgstr "檢查 '%s' 到 '%s' 的重新命名\n"
 
-#: builtin/mv.c
+#: builtin/mv.c builtin/mv.c
 msgid "bad source"
 msgstr "來源損壞"
 
-#: builtin/mv.c
+#: builtin/mv.c builtin/mv.c
 msgid "destination exists"
 msgstr "目的地已存在"
 
@@ -11772,7 +12272,7 @@
 msgid "conflicted"
 msgstr "衝突"
 
-#: builtin/mv.c
+#: builtin/mv.c builtin/mv.c
 #, c-format
 msgid "overwriting '%s'"
 msgstr "覆蓋 '%s'"
@@ -11993,7 +12493,9 @@
 msgid "could not open or read '%s'"
 msgstr "無法開啟或讀取「%s」"
 
-#: builtin/notes.c
+#: builtin/notes.c builtin/notes.c builtin/notes.c
+#: builtin/notes.c builtin/notes.c builtin/notes.c
+#: builtin/notes.c builtin/notes.c builtin/notes.c
 #, c-format
 msgid "failed to resolve '%s' as a valid ref."
 msgstr "無法解析 '%s' 為一個有效引用。"
@@ -12021,48 +12523,48 @@
 msgid "refusing to %s notes in %s (outside of refs/notes/)"
 msgstr "拒絕向 %2$s(在 refs/notes/ 之外)%1$s註解"
 
-#: builtin/notes.c
+#: builtin/notes.c builtin/notes.c
 #, c-format
 msgid "no note found for object %s."
 msgstr "未發現物件 %s 的註解。"
 
-#: builtin/notes.c
+#: builtin/notes.c builtin/notes.c
 msgid "note contents as a string"
 msgstr "註解內容作為一個字串"
 
-#: builtin/notes.c
+#: builtin/notes.c builtin/notes.c
 msgid "note contents in a file"
 msgstr "註解內容到一個檔案中"
 
-#: builtin/notes.c
+#: builtin/notes.c builtin/notes.c
 msgid "reuse and edit specified note object"
 msgstr "重用和編輯指定的註解物件"
 
-#: builtin/notes.c
+#: builtin/notes.c builtin/notes.c
 msgid "edit note message in editor"
 msgstr "在編輯器中編輯備註訊息"
 
-#: builtin/notes.c
+#: builtin/notes.c builtin/notes.c
 msgid "reuse specified note object"
 msgstr "重用指定的註解物件"
 
-#: builtin/notes.c
+#: builtin/notes.c builtin/notes.c
 msgid "allow storing empty note"
 msgstr "允許儲存空白備註"
 
-#: builtin/notes.c
+#: builtin/notes.c builtin/notes.c
 msgid "replace existing notes"
 msgstr "取代已存在的註解"
 
-#: builtin/notes.c
+#: builtin/notes.c builtin/notes.c
 msgid "<paragraph-break>"
 msgstr "<paragraph-break>"
 
-#: builtin/notes.c
+#: builtin/notes.c builtin/notes.c
 msgid "insert <paragraph-break> between paragraphs"
 msgstr "在段落間插入 <paragraph-break>"
 
-#: builtin/notes.c
+#: builtin/notes.c builtin/notes.c
 msgid "remove unnecessary whitespace"
 msgstr "移除不必要的空白字元"
 
@@ -12073,12 +12575,12 @@
 "existing notes"
 msgstr "不能新增註解。發現物件 %s 已存在註解。使用 '-f' 覆蓋現存註解"
 
-#: builtin/notes.c
+#: builtin/notes.c builtin/notes.c
 #, c-format
 msgid "Overwriting existing notes for object %s\n"
 msgstr "覆蓋物件 %s 現存註解\n"
 
-#: builtin/notes.c
+#: builtin/notes.c builtin/notes.c builtin/notes.c
 #, c-format
 msgid "Removing note for object %s\n"
 msgstr "刪除物件 %s 的註解\n"
@@ -12364,7 +12866,7 @@
 msgid "Counting objects"
 msgstr "正在計算物件數量"
 
-#: builtin/pack-objects.c pack-bitmap.c
+#: builtin/pack-objects.c pack-bitmap.c pack-bitmap.c
 #, c-format
 msgid "unable to get size of %s"
 msgstr "不能得到 %s 的大小"
@@ -12374,12 +12876,13 @@
 msgid "unable to parse object header of %s"
 msgstr "無法解析物件 %s 標頭訊息"
 
+#: builtin/pack-objects.c builtin/pack-objects.c
 #: builtin/pack-objects.c
 #, c-format
 msgid "object %s cannot be read"
 msgstr "物件 %s 無法讀取"
 
-#: builtin/pack-objects.c
+#: builtin/pack-objects.c builtin/pack-objects.c
 #, c-format
 msgid "object %s inconsistent object length (%<PRIuMAX> vs %<PRIuMAX>)"
 msgstr "物件 %s 不一致的物件長度(%<PRIuMAX> vs %<PRIuMAX>)"
@@ -12403,7 +12906,7 @@
 msgid "unable to get type of object %s"
 msgstr "無法獲得物件 %s 類型"
 
-#: builtin/pack-objects.c
+#: builtin/pack-objects.c builtin/pack-objects.c
 msgid "Compressing objects by path"
 msgstr "正在根據路徑壓縮物件"
 
@@ -12446,6 +12949,11 @@
 msgid "could not get type of object %s in pack %s"
 msgstr "無法取得 %2$s 包中 %1$s 物件的類型"
 
+#: builtin/pack-objects.c builtin/pack-objects.c
+#, c-format
+msgid "could not find pack '%s'"
+msgstr "找不到「%s」包"
+
 #: builtin/pack-objects.c
 #, c-format
 msgid "packfile %s is a promisor but --exclude-promisor-objects was given"
@@ -12453,15 +12961,10 @@
 
 #: builtin/pack-objects.c
 #, c-format
-msgid "could not find pack '%s'"
-msgstr "找不到「%s」包"
-
-#: builtin/pack-objects.c
-#, c-format
 msgid "packfile %s cannot be accessed"
 msgstr "無法存取封包檔案 %s"
 
-#: builtin/pack-objects.c
+#: builtin/pack-objects.c builtin/pack-objects.c
 msgid "Enumerating cruft objects"
 msgstr "正在枚舉無用物件"
 
@@ -12491,11 +12994,7 @@
 "預期物件 ID,卻得到垃圾資料:\n"
 " %s"
 
-#: builtin/pack-objects.c reachable.c
-msgid "could not load cruft pack .mtimes"
-msgstr "無法載入無用包 .mtimes"
-
-#: builtin/pack-objects.c
+#: builtin/pack-objects.c builtin/pack-objects.c
 msgid "cannot open pack index"
 msgstr "無法開啟包檔案索引"
 
@@ -12848,23 +13347,24 @@
 msgid "You are not currently on a branch."
 msgstr "您目前不在一個分支上。"
 
-#: builtin/pull.c
+#: builtin/pull.c builtin/pull.c
 msgid "Please specify which branch you want to rebase against."
 msgstr "請指定您要重定基底到哪一個分支。"
 
-#: builtin/pull.c
+#: builtin/pull.c builtin/pull.c
 msgid "Please specify which branch you want to merge with."
 msgstr "請指定您要合併哪一個分支。"
 
-#: builtin/pull.c
+#: builtin/pull.c builtin/pull.c
 msgid "See git-pull(1) for details."
 msgstr "詳見 git-pull(1)。"
 
-#: builtin/pull.c builtin/rebase.c
+#: builtin/pull.c builtin/pull.c builtin/pull.c
+#: builtin/rebase.c
 msgid "<remote>"
 msgstr "<遠端>"
 
-#: builtin/pull.c scalar.c
+#: builtin/pull.c builtin/pull.c builtin/pull.c scalar.c
 msgid "<branch>"
 msgstr "<分支>"
 
@@ -13271,7 +13771,8 @@
 msgid "use thin pack"
 msgstr "使用精簡打包"
 
-#: builtin/push.c builtin/send-pack.c
+#: builtin/push.c builtin/push.c builtin/send-pack.c
+#: builtin/send-pack.c
 msgid "receive pack program"
 msgstr "接收包程式"
 
@@ -13366,7 +13867,7 @@
 msgid "notes"
 msgstr "註解"
 
-#: builtin/range-diff.c
+#: builtin/range-diff.c builtin/range-diff.c builtin/range-diff.c
 msgid "passed to 'git log'"
 msgstr "傳遞給 'git log'"
 
@@ -13386,12 +13887,12 @@
 msgid "only emit output related to the second range"
 msgstr "只發出跟第二個範圍相關的輸出"
 
-#: builtin/range-diff.c
+#: builtin/range-diff.c builtin/range-diff.c builtin/range-diff.c
 #, c-format
 msgid "not a revision: '%s'"
 msgstr "非修訂版:「%s」"
 
-#: builtin/range-diff.c
+#: builtin/range-diff.c builtin/range-diff.c
 #, c-format
 msgid "not a commit range: '%s'"
 msgstr "不是提交範圍:「%s」"
@@ -13545,7 +14046,8 @@
 msgid "ignoring invalid allow_rerere_autoupdate: '%s'"
 msgstr "忽略無效的 allow_rerere_autoupdate:'%s'"
 
-#: builtin/rebase.c builtin/rm.c sequencer.c
+#: builtin/rebase.c builtin/rebase.c builtin/rebase.c
+#: builtin/rm.c sequencer.c sequencer.c
 #, c-format
 msgid "could not remove '%s'"
 msgstr "無法刪除「%s」"
@@ -13578,6 +14080,7 @@
 msgid "could not switch to %s"
 msgstr "無法切換到 %s"
 
+#: builtin/rebase.c builtin/rebase.c builtin/rebase.c
 #: builtin/rebase.c
 msgid "apply options and merge options cannot be used together"
 msgstr "套用選項與合併選項不得同時使用"
@@ -13680,7 +14183,7 @@
 msgid "synonym of --reset-author-date"
 msgstr "和 --reset-author-date 同義"
 
-#: builtin/rebase.c
+#: builtin/rebase.c builtin/rebase.c
 msgid "passed to 'git apply'"
 msgstr "傳遞給 'git apply'"
 
@@ -13688,7 +14191,7 @@
 msgid "ignore changes in whitespace"
 msgstr "忽略空白字元中的變更"
 
-#: builtin/rebase.c
+#: builtin/rebase.c builtin/rebase.c
 msgid "cherry-pick all commits, even if unchanged"
 msgstr "揀選所有提交,即使未修改"
 
@@ -14067,7 +14570,7 @@
 "                  [--dry-run | -n] [--verbose] [--all [--single-worktree] | "
 "<refs>...]"
 
-#: builtin/reflog.c
+#: builtin/reflog.c builtin/reflog.c
 #, c-format
 msgid "invalid timestamp '%s' given to '--%s'"
 msgstr "傳入「--%s」的時間戳「%s」無效"
@@ -14077,24 +14580,24 @@
 msgid "%s does not accept arguments: '%s'"
 msgstr "%s 不接受參數:'%s'"
 
-#: builtin/reflog.c
+#: builtin/reflog.c builtin/reflog.c
 msgid "do not actually prune any entries"
 msgstr "不實際剪除任何項目"
 
-#: builtin/reflog.c
+#: builtin/reflog.c builtin/reflog.c
 msgid ""
 "rewrite the old SHA1 with the new SHA1 of the entry that now precedes it"
 msgstr "將舊的 SHA1 重寫為現在比它早的新 SHA1"
 
-#: builtin/reflog.c
+#: builtin/reflog.c builtin/reflog.c
 msgid "update the reference to the value of the top reflog entry"
 msgstr "更新引用至首個引用日誌項目的值"
 
-#: builtin/reflog.c
+#: builtin/reflog.c builtin/reflog.c
 msgid "print extra information on screen"
 msgstr "在螢幕上輸出額外資訊"
 
-#: builtin/reflog.c
+#: builtin/reflog.c builtin/reflog.c
 msgid "timestamp"
 msgstr "時間戳"
 
@@ -14125,7 +14628,7 @@
 msgid "Marking reachable objects..."
 msgstr "正在標記可以取得物件..."
 
-#: builtin/reflog.c
+#: builtin/reflog.c builtin/reflog.c
 #, c-format
 msgid "reflog could not be found: '%s'"
 msgstr "找不到引用日誌:「%s」"
@@ -14260,15 +14763,15 @@
 "git remote add [-t <分支>] [-m <master>] [-f] [--tags | --no-tags] [--"
 "mirror=<fetch|push>] <名稱> <位址>"
 
-#: builtin/remote.c
+#: builtin/remote.c builtin/remote.c
 msgid "git remote rename [--[no-]progress] <old> <new>"
 msgstr "git remote rename [--[no-]progress] <old> <new>"
 
-#: builtin/remote.c
+#: builtin/remote.c builtin/remote.c
 msgid "git remote remove <name>"
 msgstr "git remote remove <名稱>"
 
-#: builtin/remote.c
+#: builtin/remote.c builtin/remote.c
 msgid "git remote set-head <name> (-a | --auto | -d | --delete | <branch>)"
 msgstr "git remote set-head <名稱> (-a | --auto | -d | --delete | <分支>)"
 
@@ -14289,19 +14792,19 @@
 msgid "git remote set-branches [--add] <name> <branch>..."
 msgstr "git remote set-branches [--add] <名稱> <分支>..."
 
-#: builtin/remote.c
+#: builtin/remote.c builtin/remote.c
 msgid "git remote get-url [--push] [--all] <name>"
 msgstr "git remote get-url [--push] [--all] <名稱>"
 
-#: builtin/remote.c
+#: builtin/remote.c builtin/remote.c
 msgid "git remote set-url [--push] <name> <newurl> [<oldurl>]"
 msgstr "git remote set-url [--push] <名稱> <新的位址> [<舊的位址>]"
 
-#: builtin/remote.c
+#: builtin/remote.c builtin/remote.c
 msgid "git remote set-url --add <name> <newurl>"
 msgstr "git remote set-url --add <名稱> <新的位址>"
 
-#: builtin/remote.c
+#: builtin/remote.c builtin/remote.c
 msgid "git remote set-url --delete <name> <url>"
 msgstr "git remote set-url --delete <名稱> <位址>"
 
@@ -14394,7 +14897,7 @@
 msgid "specifying branches to track makes sense only with fetch mirrors"
 msgstr "指定要追蹤的分支只在與取得鏡像同時使用才有意義"
 
-#: builtin/remote.c
+#: builtin/remote.c builtin/remote.c
 #, c-format
 msgid "remote %s already exists."
 msgstr "遠端 %s 已經存在。"
@@ -14404,7 +14907,8 @@
 msgid "Could not setup master '%s'"
 msgstr "無法配置 master「%s」"
 
-#: builtin/remote.c trailer.c
+#: builtin/remote.c builtin/remote.c trailer.c trailer.c
+#: trailer.c
 #, c-format
 msgid "more than one %s"
 msgstr "多於一個 %s"
@@ -14419,7 +14923,7 @@
 msgid "Could not get fetch map for refspec %s"
 msgstr "無法取得引用規格 %s 的 fetch 映射"
 
-#: builtin/remote.c
+#: builtin/remote.c builtin/remote.c
 msgid "(matching)"
 msgstr "(符合)"
 
@@ -14432,7 +14936,8 @@
 msgid "could not set '%s'"
 msgstr "無法設定「%s」"
 
-#: builtin/remote.c config.c
+#: builtin/remote.c builtin/remote.c builtin/remote.c
+#: config.c
 #, c-format
 msgid "could not unset '%s'"
 msgstr "無法取消設定「%s」"
@@ -14464,7 +14969,7 @@
 "\n"
 "若是這種情況,你可以先將遠端改為不同的名稱來解決這個問題。\n"
 
-#: builtin/remote.c
+#: builtin/remote.c builtin/remote.c builtin/remote.c
 #, c-format
 msgid "No such remote: '%s'"
 msgstr "沒有此遠端版本庫:'%s'"
@@ -14627,7 +15132,7 @@
 #. with the one in " Fetch URL: %s"
 #. translation.
 #.
-#: builtin/remote.c
+#: builtin/remote.c builtin/remote.c
 #, c-format
 msgid "  Push  URL: %s"
 msgstr "  推送位址:%s"
@@ -14636,7 +15141,7 @@
 msgid "(no URL)"
 msgstr "(無 URL)"
 
-#: builtin/remote.c
+#: builtin/remote.c builtin/remote.c builtin/remote.c
 #, c-format
 msgid "  HEAD branch: %s"
 msgstr "  HEAD 分支:%s"
@@ -14663,7 +15168,7 @@
 msgstr[0] "  遠端分支:%s"
 
 #  譯者:中文字串拼接,可刪除前導空格
-#: builtin/remote.c
+#: builtin/remote.c builtin/remote.c
 msgid " (status not queried)"
 msgstr " (狀態未查詢)"
 
@@ -14764,7 +15269,7 @@
 msgid "prune remotes after fetching"
 msgstr "抓取後剪除遠端"
 
-#: builtin/remote.c
+#: builtin/remote.c builtin/remote.c builtin/remote.c
 #, c-format
 msgid "No such remote '%s'"
 msgstr "沒有此遠端 '%s'"
@@ -14849,7 +15354,7 @@
 msgid "same as -a, pack unreachable cruft objects separately"
 msgstr "和 -a 相同,會獨立封裝無法存取的無用物件"
 
-#: builtin/repack.c
+#: builtin/repack.c builtin/repack.c
 msgid "approxidate"
 msgstr "近似日期"
 
@@ -15201,60 +15706,48 @@
 
 #: builtin/replay.c
 #, c-format
-msgid "'%s' is not a valid commit-ish for %s"
-msgstr "「%s」不是 %s 有效的提交指示元"
-
-#: builtin/replay.c
-msgid "need some commits to replay"
-msgstr "需要一些提交才能重放"
-
-#: builtin/replay.c
-msgid "all positive revisions given must be references"
-msgstr "提供的所有正向修訂集必須為引用"
-
-#: builtin/replay.c
-msgid "argument to --advance must be a reference"
-msgstr "傳入 --advance 的引數必須為引用"
-
-#: builtin/replay.c
-msgid ""
-"cannot advance target with multiple sources because ordering would be ill-"
-"defined"
-msgstr "無法用多個來源演進目的地,以免無法確定排序"
-
-#: builtin/replay.c
-#, c-format
 msgid "invalid %s value: '%s'"
 msgstr "無效的 %s 值:「%s」"
 
 #: builtin/replay.c
 msgid ""
-"(EXPERIMENTAL!) git replay ([--contained] --onto <newbase> | --advance "
-"<branch>) [--ref-action[=<mode>]] <revision-range>"
+"(EXPERIMENTAL!) git replay ([--contained] --onto=<newbase> | --"
+"advance=<branch> | --revert=<branch>)\n"
+"[--ref=<ref>] [--ref-action=<mode>] <revision-range>"
 msgstr ""
-"(實驗性功能!) git replay ([--contained] --onto <newbase> | --advance "
-"<branch>) [--ref-action[=<mode>]] <revision-range>"
-
-#: builtin/replay.c
-msgid "make replay advance given branch"
-msgstr "在指定分支上進行重放演進"
-
-#: builtin/replay.c
-msgid "replay onto given commit"
-msgstr "重放到指定提交"
+"(實驗功能!)git replay ([--contained] --onto=<newbase> | --"
+"advance=<branch> | --revert=<branch>)\n"
+"[--ref=<ref>] [--ref-action=<mode>] <revision-range>"
 
 #: builtin/replay.c
 msgid "update all branches that point at commits in <revision-range>"
 msgstr "更新所有指向 <revision-range> 範圍中提交的分支"
 
 #: builtin/replay.c
+msgid "replay onto given commit"
+msgstr "重放到指定提交"
+
+#: builtin/replay.c
+msgid "make replay advance given branch"
+msgstr "在指定分支上進行重放演進"
+
+#: builtin/replay.c
+msgid "revert commits onto given branch"
+msgstr "將提交還原至指定的分支"
+
+#: builtin/replay.c
+msgid "reference to update with result"
+msgstr "要以結果更新的引用"
+
+#: builtin/replay.c
 msgid "control ref update behavior (update|print)"
 msgstr "控制引用的更新行為(update|print)"
 
 #: builtin/replay.c
-msgid "option --onto or --advance is mandatory"
-msgstr "必須傳入 --onto 或 --advance 選項"
+msgid "exactly one of --onto, --advance, or --revert is required"
+msgstr "--onto、--advance 或 --revert 必須三選一"
 
+#: builtin/replay.c builtin/replay.c builtin/replay.c
 #: builtin/replay.c
 #, c-format
 msgid ""
@@ -15262,48 +15755,25 @@
 "will be forced"
 msgstr "將覆寫部分修訂版遍歷選項,強制使用「struct rev_info」的「%s」位元"
 
-#: builtin/replay.c
-#, c-format
-msgid "failed to begin ref transaction: %s"
-msgstr "無法開始引用事務:%s"
-
-#: builtin/replay.c
-msgid "error preparing revisions"
-msgstr "無法準備修訂集"
-
-#: builtin/replay.c
-msgid "replaying down from root commit is not supported yet!"
-msgstr "尚不支援從根提交重放!"
-
-#: builtin/replay.c
-msgid "replaying merge commits is not supported yet!"
-msgstr "尚不支援重放合併提交!"
-
-#: builtin/replay.c
-#, c-format
-msgid "failed to update ref '%s': %s"
-msgstr "無法更新「%s」引用:%s"
-
-#: builtin/replay.c
-#, c-format
-msgid "failed to commit ref transaction: %s"
-msgstr "無法提交引用事務:%s"
-
 #: builtin/repo.c
 #, c-format
 msgid "key '%s' not found"
 msgstr "找不到「%s」鍵"
 
 #: builtin/repo.c
+msgid "--keys can only be used with --format=lines or --format=nul"
+msgstr "--keys 只能與 --format=lines 或 --format=nul 一起使用"
+
+#: builtin/repo.c
 #, c-format
 msgid "invalid format '%s'"
 msgstr "「%s」格式無效"
 
-#: builtin/repo.c
+#: builtin/repo.c builtin/repo.c
 msgid "output format"
 msgstr "輸出格式"
 
-#: builtin/repo.c
+#: builtin/repo.c builtin/repo.c
 msgid "synonym for --format=nul"
 msgstr "和 --format=nul 同義"
 
@@ -15312,6 +15782,14 @@
 msgstr "輸出所有鍵值對"
 
 #: builtin/repo.c
+msgid "show keys"
+msgstr "顯示設定鍵"
+
+#: builtin/repo.c
+msgid "--keys cannot be used with a <key> or --all"
+msgstr "--keys 不能與 <key> 或 --all 一起使用"
+
+#: builtin/repo.c
 msgid "unsupported output format"
 msgstr "不支援的輸出格式"
 
@@ -15323,7 +15801,7 @@
 msgid "References"
 msgstr "引用"
 
-#: builtin/repo.c
+#: builtin/repo.c builtin/repo.c
 msgid "Count"
 msgstr "總數"
 
@@ -15331,6 +15809,7 @@
 msgid "Branches"
 msgstr "分支"
 
+#: builtin/repo.c builtin/repo.c builtin/repo.c builtin/repo.c
 #: builtin/repo.c
 msgid "Tags"
 msgstr "標籤"
@@ -15347,15 +15826,15 @@
 msgid "Reachable objects"
 msgstr "可到達物件"
 
-#: builtin/repo.c
+#: builtin/repo.c builtin/repo.c builtin/repo.c builtin/repo.c
 msgid "Commits"
 msgstr "提交"
 
-#: builtin/repo.c
+#: builtin/repo.c builtin/repo.c builtin/repo.c builtin/repo.c
 msgid "Trees"
 msgstr "樹狀物件"
 
-#: builtin/repo.c
+#: builtin/repo.c builtin/repo.c builtin/repo.c builtin/repo.c
 msgid "Blobs"
 msgstr "資料物件"
 
@@ -15368,6 +15847,22 @@
 msgstr "磁碟大小"
 
 #: builtin/repo.c
+msgid "Largest objects"
+msgstr "最大的物件"
+
+#: builtin/repo.c builtin/repo.c builtin/repo.c builtin/repo.c
+msgid "Maximum size"
+msgstr "最大大小"
+
+#: builtin/repo.c
+msgid "Maximum parents"
+msgstr "最大父提交數"
+
+#: builtin/repo.c
+msgid "Maximum entries"
+msgstr "最大項目數"
+
+#: builtin/repo.c
 msgid "Repository structure"
 msgstr "版本庫結構"
 
@@ -15461,7 +15956,8 @@
 msgid "Cannot do a %s reset in the middle of a merge."
 msgstr "在合併過程中不能做%s重設動作。"
 
-#: builtin/reset.c builtin/stash.c
+#: builtin/reset.c builtin/stash.c builtin/stash.c
+#: builtin/stash.c
 msgid "be quiet, only report errors"
 msgstr "安靜模式,只報告錯誤"
 
@@ -15477,7 +15973,7 @@
 msgid "reset only HEAD"
 msgstr "只重設 HEAD"
 
-#: builtin/reset.c
+#: builtin/reset.c builtin/reset.c
 msgid "reset HEAD, index and working tree"
 msgstr "重設 HEAD、索引和工作區"
 
@@ -15801,7 +16297,7 @@
 msgid_plural "the following files have changes staged in the index:"
 msgstr[0] "下列檔案有暫存在索引的變更:"
 
-#: builtin/rm.c
+#: builtin/rm.c builtin/rm.c
 msgid ""
 "\n"
 "(use --cached to keep the file, or -f to force removal)"
@@ -15898,7 +16394,7 @@
 msgid "using multiple --group options with stdin is not supported"
 msgstr "不支援在標準輸入使用多個 --group 選項"
 
-#: builtin/shortlog.c
+#: builtin/shortlog.c builtin/shortlog.c
 #, c-format
 msgid "using %s with stdin is not supported"
 msgstr "不支援對 %s 使用 stdin"
@@ -16080,6 +16576,55 @@
 msgid "Unknown hash algorithm"
 msgstr "未知的雜湊算法"
 
+#: builtin/show-index.c
+msgid "assuming SHA-1; use --object-format to override"
+msgstr "假設使用 SHA-1,使用 --object-format 可以覆寫"
+
+#: builtin/show-index.c
+msgid "unable to read header"
+msgstr "無法讀取標頭"
+
+#: builtin/show-index.c
+msgid "unknown index version"
+msgstr "未知的索引版本"
+
+#: builtin/show-index.c builtin/show-index.c
+msgid "unable to read index"
+msgstr "無法讀取索引"
+
+#: builtin/show-index.c
+msgid "corrupt index file"
+msgstr "索引檔案損壞"
+
+#: builtin/show-index.c
+#, c-format
+msgid "unable to read entry %u/%u"
+msgstr "無法讀取項目 %u/%u"
+
+#: builtin/show-index.c
+#, c-format
+msgid "unable to read sha1 %u/%u"
+msgstr "無法讀取 sha1 %u/%u"
+
+#: builtin/show-index.c
+#, c-format
+msgid "unable to read crc %u/%u"
+msgstr "無法讀取 crc %u/%u"
+
+#: builtin/show-index.c
+#, c-format
+msgid "unable to read 32b offset %u/%u"
+msgstr "無法讀取 32 位元偏移 %u/%u"
+
+#: builtin/show-index.c
+msgid "inconsistent 64b offset index"
+msgstr "64 位元偏移索引不一致"
+
+#: builtin/show-index.c
+#, c-format
+msgid "unable to read 64b offset %u"
+msgstr "無法讀取 64 位元偏移 %u"
+
 #: builtin/show-ref.c
 msgid ""
 "git show-ref [--head] [-d | --dereference]\n"
@@ -16124,7 +16669,7 @@
 msgid "stricter reference checking, requires exact ref path"
 msgstr "更嚴格的引用檢測,需要精確的引用路徑"
 
-#: builtin/show-ref.c
+#: builtin/show-ref.c builtin/show-ref.c
 msgid "show the HEAD reference, even if it would be filtered out"
 msgstr "顯示 HEAD 引用,即使被過濾掉"
 
@@ -16189,10 +16734,12 @@
 msgid "failed to modify sparse-index config"
 msgstr "無法修改稀疏索引設定"
 
+#: builtin/sparse-checkout.c builtin/sparse-checkout.c
 #: builtin/sparse-checkout.c
 msgid "initialize the sparse-checkout in cone mode"
 msgstr "以 cone 模式初始化稀疏簽出"
 
+#: builtin/sparse-checkout.c builtin/sparse-checkout.c
 #: builtin/sparse-checkout.c
 msgid "toggle the use of a sparse index"
 msgstr "切換是否使用稀疏索引"
@@ -16207,11 +16754,12 @@
 msgid "could not normalize path %s"
 msgstr "無法標準化路徑 %s"
 
-#: builtin/sparse-checkout.c
+#: builtin/sparse-checkout.c builtin/sparse-checkout.c
 #, c-format
 msgid "unable to unquote C-style string '%s'"
 msgstr "無法去掉 '%s' C 樣式字串的引號"
 
+#: builtin/sparse-checkout.c builtin/sparse-checkout.c
 #: builtin/sparse-checkout.c
 msgid "unable to load existing sparse-checkout patterns"
 msgstr "無法載入現存的稀疏簽出樣式"
@@ -16262,12 +16810,12 @@
 msgid "git sparse-checkout add [--skip-checks] (--stdin | <patterns>)"
 msgstr "git sparse-checkout add [--skip-checks] (--stdin | <patterns>)"
 
-#: builtin/sparse-checkout.c
+#: builtin/sparse-checkout.c builtin/sparse-checkout.c
 msgid ""
 "skip some sanity checks on the given paths that might give false positives"
 msgstr "在可能給出誤判結果的指定目錄,略過某些完整性檢查"
 
-#: builtin/sparse-checkout.c
+#: builtin/sparse-checkout.c builtin/sparse-checkout.c
 msgid "read patterns from standard in"
 msgstr "從標準輸入讀取樣式"
 
@@ -16377,19 +16925,19 @@
 
 #: builtin/stash.c
 msgid ""
-"git stash [push [-p | --patch] [-S | --staged] [-k | --[no-]keep-index] [-q "
+"git stash [push] [-p | --patch] [-S | --staged] [-k | --[no-]keep-index] [-q "
 "| --quiet]\n"
 "          [-u | --include-untracked] [-a | --all] [(-m | --message) "
 "<message>]\n"
 "          [--pathspec-from-file=<file> [--pathspec-file-nul]]\n"
-"          [--] [<pathspec>...]]"
+"          [--] [<pathspec>...]"
 msgstr ""
-"git stash [push [-p | --patch] [-S | --staged] [-k | --[no-]keep-index] [-q "
+"git stash [push] [-p | --patch] [-S | --staged] [-k | --[no-]keep-index] [-q "
 "| --quiet]\n"
 "          [-u | --include-untracked] [-a | --all] [(-m | --message) "
 "<message>]\n"
 "          [--pathspec-from-file=<file> [--pathspec-file-nul]]\n"
-"          [--] [<pathspec>...]]"
+"          [--] [<pathspec>...]"
 
 #: builtin/stash.c
 msgid ""
@@ -16477,7 +17025,7 @@
 msgid "could not restore untracked files from stash"
 msgstr "無法從貯存條目中復原未追蹤檔案"
 
-#: builtin/stash.c
+#: builtin/stash.c builtin/stash.c
 msgid "attempt to recreate the index"
 msgstr "嘗試重建索引"
 
@@ -16520,12 +17068,12 @@
 msgid "only show untracked files in the stash"
 msgstr "只在貯存區顯示未追蹤檔案"
 
-#: builtin/stash.c
+#: builtin/stash.c builtin/stash.c
 #, c-format
 msgid "Cannot update %s with %s"
 msgstr "無法用 %2$s 更新 %1$s"
 
-#: builtin/stash.c
+#: builtin/stash.c builtin/stash.c builtin/stash.c
 msgid "stash message"
 msgstr "貯存說明"
 
@@ -16553,7 +17101,7 @@
 msgid "Cannot save the untracked files"
 msgstr "無法儲存未追蹤檔案"
 
-#: builtin/stash.c
+#: builtin/stash.c builtin/stash.c
 msgid "Cannot save the current worktree state"
 msgstr "無法儲存目前工作區狀態"
 
@@ -16598,30 +17146,31 @@
 msgid "Cannot remove worktree changes"
 msgstr "無法刪除工作區變更"
 
-#: builtin/stash.c
+#: builtin/stash.c builtin/stash.c
 msgid "keep index"
 msgstr "保持索引"
 
-#: builtin/stash.c
+#: builtin/stash.c builtin/stash.c
 msgid "stash staged changes only"
 msgstr "只貯存暫存變更"
 
-#: builtin/stash.c
+#: builtin/stash.c builtin/stash.c
 msgid "stash in patch mode"
 msgstr "以修補檔模式貯存"
 
-#: builtin/stash.c
+#: builtin/stash.c builtin/stash.c
 msgid "quiet mode"
 msgstr "靜默模式"
 
-#: builtin/stash.c
+#: builtin/stash.c builtin/stash.c
 msgid "include untracked files in stash"
 msgstr "貯存中包含未追蹤檔案"
 
-#: builtin/stash.c
+#: builtin/stash.c builtin/stash.c
 msgid "include ignore files"
 msgstr "包含忽略的檔案"
 
+#: builtin/stash.c builtin/stash.c builtin/stash.c
 #: builtin/stash.c
 #, c-format
 msgid "cannot parse commit %s"
@@ -16666,7 +17215,7 @@
 msgid "cannot parse parents of commit: %s"
 msgstr "無法解析提交的父物件:%s"
 
-#: builtin/stash.c
+#: builtin/stash.c builtin/stash.c builtin/stash.c
 #, c-format
 msgid "%s does not look like a stash commit"
 msgstr "%s 似乎不是貯存提交"
@@ -16717,12 +17266,17 @@
 "authoritative upstream."
 msgstr "找不到「%s」組態設定。假定這個版本庫是其自身的官方上游。"
 
-#: builtin/submodule--helper.c
+#: builtin/submodule--helper.c builtin/submodule--helper.c
 #, c-format
 msgid "could not get a repository handle for submodule '%s'"
 msgstr "無法取得子模組「%s」的版本庫控制代碼"
 
 #: builtin/submodule--helper.c
+msgid "git submodule--helper get-default-remote <path>"
+msgstr "git submodule--helper get-default-remote <path>"
+
+#: builtin/submodule--helper.c builtin/submodule--helper.c
+#: builtin/submodule--helper.c
 #, c-format
 msgid "No url found for submodule path '%s' in .gitmodules"
 msgstr "在 .gitmodules 中未找到子模組 '%s' 的 url"
@@ -16755,6 +17309,7 @@
 msgid "suppress output of entering each submodule command"
 msgstr "隱藏每個子模組進入命令的輸出"
 
+#: builtin/submodule--helper.c builtin/submodule--helper.c
 #: builtin/submodule--helper.c
 msgid "recurse into nested submodules"
 msgstr "遞迴進入嵌套子模組中"
@@ -16765,6 +17320,17 @@
 
 #: builtin/submodule--helper.c
 #, c-format
+msgid ""
+"failed to set a valid default config for 'submodule.%s.gitdir'. Please "
+"ensure it is set, for example by running something like: 'git config "
+"submodule.%s.gitdir .git/modules/%s'"
+msgstr ""
+"無法為「submodule.%s.gitdir」設定有效的預設組態。請確定是否設定完成,舉例來"
+"說,可以執行類似這樣的命令進行設定:「git config submodule.%s.gitdir .git/"
+"modules/%s」"
+
+#: builtin/submodule--helper.c
+#, c-format
 msgid "Failed to register url for submodule path '%s'"
 msgstr "無法為子模組 '%s' 註冊 url"
 
@@ -16791,6 +17357,7 @@
 msgid "git submodule init [<options>] [<path>]"
 msgstr "git submodule init [<options>] [<path>]"
 
+#: builtin/submodule--helper.c builtin/submodule--helper.c
 #: builtin/submodule--helper.c
 #, c-format
 msgid "no submodule mapping found in .gitmodules for path '%s'"
@@ -16801,12 +17368,12 @@
 msgid "could not resolve HEAD ref inside the submodule '%s'"
 msgstr "無法解析子模組「%s」的 HEAD 引用"
 
-#: builtin/submodule--helper.c
+#: builtin/submodule--helper.c builtin/submodule--helper.c
 #, c-format
 msgid "failed to recurse into submodule '%s'"
 msgstr "遞迴子模組 '%s' 失敗"
 
-#: builtin/submodule--helper.c
+#: builtin/submodule--helper.c builtin/submodule--helper.c
 msgid "suppress submodule status output"
 msgstr "隱藏子模組的狀態輸出"
 
@@ -16870,6 +17437,30 @@
 msgstr "無法取得 HEAD 的修訂版"
 
 #: builtin/submodule--helper.c
+msgid "git submodule--helper gitdir <name>"
+msgstr "git submodule--helper gitdir <name>"
+
+#: builtin/submodule--helper.c
+msgid ""
+"could not set core.repositoryformatversion to 1.\n"
+"Please set it for migration to work, for example:\n"
+"git config core.repositoryformatversion 1"
+msgstr ""
+"無法將 core.repositoryformatversion 設定為 1。\n"
+"請設定以便進行遷移,例如:\n"
+"git config core.repositoryformatversion 1"
+
+#: builtin/submodule--helper.c
+msgid ""
+"could not enable submodulePathConfig extension. It is required\n"
+"for migration to work. Please enable it in the root repo:\n"
+"git config extensions.submodulePathConfig true"
+msgstr ""
+"無法啟用 submodulePathConfig 擴充功能。\n"
+"遷移需要這個功能才能運作,請在根版本庫中啟用:\n"
+"git config extensions.submodulePathConfig true"
+
+#: builtin/submodule--helper.c
 #, c-format
 msgid "Synchronizing submodule url for '%s'\n"
 msgstr "為 '%s' 同步子模組 url\n"
@@ -16962,7 +17553,7 @@
 msgid "could not get a repository handle for gitdir '%s'"
 msgstr "無法取得 gitdir「%s」的版本庫控制代碼"
 
-#: builtin/submodule--helper.c
+#: builtin/submodule--helper.c builtin/submodule--helper.c
 #, c-format
 msgid "submodule '%s' cannot add alternate: %s"
 msgstr "子模組 '%s' 不能新增版本庫備選:%s"
@@ -16977,11 +17568,7 @@
 msgid "Value '%s' for submodule.alternateLocation is not recognized"
 msgstr "不能識別 submodule.alternateLocation 的取值 '%s'"
 
-#: builtin/submodule--helper.c submodule.c
-#, c-format
-msgid "refusing to create/use '%s' in another submodule's git dir"
-msgstr "拒絕在其他子模組的 git 目錄中建立/使用「%s」"
-
+#: builtin/submodule--helper.c builtin/submodule--helper.c
 #: builtin/submodule--helper.c
 #, c-format
 msgid "directory not empty: '%s'"
@@ -16992,6 +17579,15 @@
 msgid "clone of '%s' into submodule path '%s' failed"
 msgstr "無法拓製「%s」到子模組路徑「%s」"
 
+#: builtin/submodule--helper.c submodule.c
+#, c-format
+msgid ""
+"refusing to create/use '%s' in another submodule's git dir. Enabling "
+"extensions.submodulePathConfig should fix this."
+msgstr ""
+"拒絕在另一個子模組的 git 目錄中建立或使用「%s」。啟用 "
+"extensions.submodulePathConfig 應當可以修正此問題。"
+
 #: builtin/submodule--helper.c
 #, c-format
 msgid "could not get submodule directory for '%s'"
@@ -17013,10 +17609,11 @@
 msgid "url where to clone the submodule from"
 msgstr "拓製子模組的 url 位址"
 
-#: builtin/submodule--helper.c
+#: builtin/submodule--helper.c builtin/submodule--helper.c
 msgid "depth for shallow clones"
 msgstr "淺拓製的深度"
 
+#: builtin/submodule--helper.c builtin/submodule--helper.c
 #: builtin/submodule--helper.c
 msgid "force cloning progress"
 msgstr "強制顯示拓製進度"
@@ -17263,7 +17860,7 @@
 msgid "--branch or --default required"
 msgstr "需要 --branch 或 --default"
 
-#: builtin/submodule--helper.c
+#: builtin/submodule--helper.c builtin/submodule--helper.c
 msgid "print only error messages"
 msgstr "只輸出錯誤訊息"
 
@@ -17328,7 +17925,7 @@
 msgid "unable to checkout submodule '%s'"
 msgstr "無法簽出「%s」子模組"
 
-#: builtin/submodule--helper.c
+#: builtin/submodule--helper.c builtin/submodule--helper.c
 msgid "please make sure that the .gitmodules file is in the working tree"
 msgstr "請確認 .gitmodules 檔案在工作區裡"
 
@@ -17337,6 +17934,7 @@
 msgid "Failed to add submodule '%s'"
 msgstr "無法加入子模組「%s」"
 
+#: builtin/submodule--helper.c builtin/submodule--helper.c
 #: builtin/submodule--helper.c
 #, c-format
 msgid "Failed to register submodule '%s'"
@@ -17530,7 +18128,7 @@
 msgid "no tag message?"
 msgstr "無標籤說明?"
 
-#: builtin/tag.c
+#: builtin/tag.c builtin/tag.c
 #, c-format
 msgid "The tag message has been left in %s\n"
 msgstr "標籤說明被保留在 %s\n"
@@ -17591,11 +18189,11 @@
 msgid "show tag list in columns"
 msgstr "以列的方式顯示標籤列表"
 
-#: builtin/tag.c
+#: builtin/tag.c builtin/tag.c
 msgid "print only tags that contain the commit"
 msgstr "只列印包含該提交的標籤"
 
-#: builtin/tag.c
+#: builtin/tag.c builtin/tag.c
 msgid "print only tags that don't contain the commit"
 msgstr "只列印不包含該提交的標籤"
 
@@ -17671,7 +18269,7 @@
 msgid "failed to delete file %s"
 msgstr "刪除檔案 %s 失敗"
 
-#: builtin/update-index.c
+#: builtin/update-index.c builtin/update-index.c
 #, c-format
 msgid "failed to delete directory %s"
 msgstr "刪除目錄 %s 失敗"
@@ -18089,8 +18687,8 @@
 msgstr "報告剪除的工作區"
 
 #: builtin/worktree.c
-msgid "expire working trees older than <time>"
-msgstr "將早於 <時間> 的工作區過期"
+msgid "prune missing working trees older than <time>"
+msgstr "清除早於 <time> 的遺失工作區"
 
 #: builtin/worktree.c
 #, c-format
@@ -18130,7 +18728,7 @@
 msgid "failed to copy worktree config from '%s' to '%s'"
 msgstr "將工作區組態從「%s」複製至「%s」失敗"
 
-#: builtin/worktree.c
+#: builtin/worktree.c builtin/worktree.c
 #, c-format
 msgid "failed to unset '%s' in '%s'"
 msgstr "無法取消「%2$s」中「%1$s」的設定"
@@ -18149,7 +18747,7 @@
 msgid "could not find created worktree '%s'"
 msgstr "找不到建立的工作區「%s」"
 
-#: builtin/worktree.c
+#: builtin/worktree.c builtin/worktree.c
 #, c-format
 msgid "Preparing worktree (new branch '%s')"
 msgstr "準備工作區(新分支 '%s')"
@@ -18170,15 +18768,8 @@
 msgstr "準備工作區(分離開頭指標 %s)"
 
 #: builtin/worktree.c
-#, c-format
-msgid ""
-"HEAD points to an invalid (or orphaned) reference.\n"
-"HEAD path: '%s'\n"
-"HEAD contents: '%s'"
-msgstr ""
-"HEAD 指向無效(或孤立)引用。\n"
-"HEAD 路徑:「%s」\n"
-"HEAD 內容:「%s」"
+msgid "HEAD points to an invalid (or orphaned) reference.\n"
+msgstr "HEAD 指向無效(或孤立)引用。\n"
 
 #: builtin/worktree.c
 msgid ""
@@ -18212,7 +18803,7 @@
 msgid "keep the new working tree locked"
 msgstr "鎖定新工作區"
 
-#: builtin/worktree.c
+#: builtin/worktree.c builtin/worktree.c
 msgid "reason for locking"
 msgstr "鎖定原因"
 
@@ -18224,7 +18815,7 @@
 msgid "try to match the new branch name with a remote-tracking branch"
 msgstr "嘗試為新分支名符合一個遠端追蹤分支"
 
-#: builtin/worktree.c
+#: builtin/worktree.c builtin/worktree.c builtin/worktree.c
 msgid "use relative paths for worktrees"
 msgstr "對工作區使用相對路徑"
 
@@ -18236,7 +18827,7 @@
 #: builtin/worktree.c
 #, c-format
 msgid "option '%s' and commit-ish cannot be used together"
-msgstr "「%s」選項和提交編號不得同時使用"
+msgstr "「%s」選項和提交指示元不得同時使用"
 
 #: builtin/worktree.c
 msgid "added with --lock"
@@ -18251,19 +18842,20 @@
 msgstr "如果有則顯示延伸的註釋和原因"
 
 #: builtin/worktree.c
-msgid "add 'prunable' annotation to worktrees older than <time>"
-msgstr "對舊於 <時間> 的工作區加上 ‘prunable’ 標示"
+msgid "add 'prunable' annotation to missing worktrees older than <time>"
+msgstr "對舊於 <time> 的遺失工作區加上「prunable」標示"
 
 #: builtin/worktree.c
 msgid "terminate records with a NUL character"
 msgstr "以一個 NUL 字元終止記錄"
 
+#: builtin/worktree.c builtin/worktree.c builtin/worktree.c
 #: builtin/worktree.c
 #, c-format
 msgid "'%s' is not a working tree"
 msgstr "'%s' 不是一個工作區"
 
-#: builtin/worktree.c
+#: builtin/worktree.c builtin/worktree.c
 msgid "The main working tree cannot be locked or unlocked"
 msgstr "主工作區無法被加鎖或解鎖"
 
@@ -18290,7 +18882,7 @@
 msgid "force move even if worktree is dirty or locked"
 msgstr "強制移動,即使工作區是髒的或已鎖定"
 
-#: builtin/worktree.c
+#: builtin/worktree.c builtin/worktree.c
 #, c-format
 msgid "'%s' is a main working tree"
 msgstr "'%s' 是一個主工作區"
@@ -18870,6 +19462,10 @@
 msgstr "顯示 Git 的說明訊息"
 
 #: command-list.h
+msgid "EXPERIMENTAL: Rewrite history"
+msgstr "實驗性:重寫歷史記錄"
+
+#: command-list.h
 msgid "Run git hooks"
 msgstr "執行 git 掛鉤"
 
@@ -19007,8 +19603,8 @@
 msgstr "打包頭和標籤以實現高效的版本庫存取"
 
 #: command-list.h
-msgid "Compute unique ID for a patch"
-msgstr "計算一個修補檔的唯一 ID"
+msgid "Compute unique IDs for patches"
+msgstr "計算修補檔案的唯一 ID"
 
 #: command-list.h
 msgid "Prune all unreachable objects from the object database"
@@ -19491,7 +20087,7 @@
 msgid "unable to find all commit-graph files"
 msgstr "無法找到所有提交圖檔案"
 
-#: commit-graph.c
+#: commit-graph.c commit-graph.c
 msgid "invalid commit position. commit-graph is likely corrupt"
 msgstr "無效的提交位置。提交圖可能已損壞"
 
@@ -19637,7 +20233,7 @@
 msgid "commit-graph has incorrect OID order: %s then %s"
 msgstr "提交圖的物件 ID 順序不正確:%s 然後 %s"
 
-#: commit-graph.c
+#: commit-graph.c commit-graph.c
 #, c-format
 msgid "commit-graph has incorrect fanout value: fanout[%d] = %u != %u"
 msgstr "提交圖有不正確的扇出值:fanout[%d] = %u != %u"
@@ -19693,11 +20289,17 @@
 msgid "Verifying commits in commit graph"
 msgstr "正在驗證提交圖中的提交"
 
-#: commit-reach.c sequencer.c
+#: commit-reach.c commit-reach.c commit-reach.c sequencer.c
+#: sequencer.c
 #, c-format
 msgid "could not parse commit %s"
 msgstr "無法解析提交 %s"
 
+#: commit.c object.c
+#, c-format
+msgid "object %s is a %s, not a %s"
+msgstr "物件 %s 是一個 %s,不是一個 %s"
+
 #: commit.c
 #, c-format
 msgid "%s %s is not a commit!"
@@ -19766,7 +20368,7 @@
 msgid "no libc information available\n"
 msgstr "沒有可用的 libc 資訊\n"
 
-#: compat/disk.h
+#: compat/disk.h compat/disk.h
 #, c-format
 msgid "could not determine free disk size for '%s'"
 msgstr "無法判斷「%s」的剩餘磁碟大小"
@@ -19786,7 +20388,8 @@
 msgid "[GLE %ld] health thread getting BHFI for '%ls'"
 msgstr "[GLE %ld] 健康監聽執行緒取得「%ls」的 BHFI"
 
-#: compat/fsmonitor/fsm-health-win32.c compat/fsmonitor/fsm-listen-win32.c
+#: compat/fsmonitor/fsm-health-win32.c
+#: compat/fsmonitor/fsm-listen-win32.c
 #, c-format
 msgid "could not convert to wide characters: '%s'"
 msgstr "無法轉換至較寬字元:「%s」"
@@ -19889,7 +20492,7 @@
 msgid "failed to get owner for '%s' (%ld)"
 msgstr "無法取得「%s」的所有者 (%ld)"
 
-#: compat/obstack.c
+#: compat/obstack.c compat/obstack.c
 msgid "memory exhausted"
 msgstr "記憶體耗盡"
 
@@ -20086,12 +20689,12 @@
 msgid "empty config key"
 msgstr "空白設定鍵"
 
-#: config.c
+#: config.c config.c
 #, c-format
 msgid "bogus config parameter: %s"
 msgstr "偽設定參數:%s"
 
-#: config.c
+#: config.c config.c config.c config.c
 #, c-format
 msgid "bogus format in %s"
 msgstr "%s 中格式錯誤"
@@ -20191,11 +20794,6 @@
 
 #: config.c
 #, c-format
-msgid "bad boolean config value '%s' for '%s'"
-msgstr "「%2$s」的「%1$s」布林設定值無效"
-
-#: config.c
-#, c-format
 msgid "failed to expand user dir in: '%s'"
 msgstr "無法展開使用者目錄於:’%s’"
 
@@ -20309,7 +20907,7 @@
 msgid "no multi-line comment allowed: '%s'"
 msgstr "不允許多列備註:「%s」"
 
-#: config.c
+#: config.c config.c
 #, c-format
 msgid "could not lock config file %s"
 msgstr "無法鎖定組態檔案 %s"
@@ -20324,7 +20922,7 @@
 msgid "invalid config file %s"
 msgstr "無效的設定檔案 %s"
 
-#: config.c
+#: config.c config.c
 #, c-format
 msgid "fstat on %s failed"
 msgstr "對 %s 呼叫 fstat 失敗"
@@ -20334,12 +20932,12 @@
 msgid "unable to mmap '%s'%s"
 msgstr "無法 mmap '%s'%s"
 
-#: config.c
+#: config.c config.c
 #, c-format
 msgid "chmod on %s failed"
 msgstr "對 %s 呼叫 chmod 失敗"
 
-#: config.c
+#: config.c config.c
 #, c-format
 msgid "could not write config file %s"
 msgstr "無法寫入組態檔案 %s"
@@ -20434,7 +21032,7 @@
 msgid "expected flush after bundle-uri listing"
 msgstr "在 bundle-uri 清單後應該有一個 flush 包"
 
-#: connect.c
+#: connect.c connect.c
 msgid "expected response end packet after ref listing"
 msgstr "在引用列表後預期要有回應結束封包"
 
@@ -20456,7 +21054,7 @@
 msgid "unable to set SO_KEEPALIVE on socket"
 msgstr "無法為 socket 設定 SO_KEEPALIVE"
 
-#: connect.c
+#: connect.c connect.c
 #, c-format
 msgid "Looking up %s ... "
 msgstr "尋找 %s ... "
@@ -20467,7 +21065,7 @@
 msgstr "無法尋找 %s(埠 %s)(%s)"
 
 #. TRANSLATORS: this is the end of "Looking up %s ... "
-#: connect.c
+#: connect.c connect.c
 #, c-format
 msgid ""
 "done.\n"
@@ -20476,7 +21074,7 @@
 "完成。\n"
 "連線到 %s(埠 %s)... "
 
-#: connect.c
+#: connect.c connect.c
 #, c-format
 msgid ""
 "unable to connect to %s:\n"
@@ -20486,7 +21084,7 @@
 "%s"
 
 #. TRANSLATORS: this is the end of "Connecting to %s (port %s) ... "
-#: connect.c
+#: connect.c connect.c
 msgid "done."
 msgstr "完成。"
 
@@ -20500,7 +21098,7 @@
 msgid "unknown port %s"
 msgstr "未知埠 %s"
 
-#: connect.c
+#: connect.c connect.c
 #, c-format
 msgid "strange hostname '%s' blocked"
 msgstr "已阻止奇怪的主機名稱 '%s'"
@@ -20611,7 +21209,7 @@
 "檔案 '%s' 缺少一個位元組順序標記(BOM)。請使用 UTF-%sBE or UTF-%sLE(取決於"
 "字節序)作為工作區編碼。"
 
-#: convert.c
+#: convert.c convert.c
 #, c-format
 msgid "failed to encode '%s' from %s to %s"
 msgstr "無法對 '%s' 進行從 %s 到 %s 的編碼"
@@ -20636,12 +21234,12 @@
 msgid "external filter '%s' failed %d"
 msgstr "外部過濾器 '%s' 失敗碼 %d"
 
-#: convert.c
+#: convert.c convert.c
 #, c-format
 msgid "read from external filter '%s' failed"
 msgstr "從外部過濾器 '%s' 讀取失敗"
 
-#: convert.c
+#: convert.c convert.c
 #, c-format
 msgid "external filter '%s' failed"
 msgstr "外部過濾器 '%s' 失敗"
@@ -20665,7 +21263,7 @@
 msgid "true/false are no valid working-tree-encodings"
 msgstr "true/false 不是有效的工作區編碼"
 
-#: convert.c
+#: convert.c convert.c
 #, c-format
 msgid "%s: clean filter '%s' failed"
 msgstr "%s:clean 過濾器 '%s' 失敗"
@@ -20771,7 +21369,7 @@
 msgid_plural "%s, %<PRIuMAX> months ago"
 msgstr[0] "%s %<PRIuMAX> 個月前"
 
-#: date.c
+#: date.c date.c
 #, c-format
 msgid "%<PRIuMAX> year ago"
 msgid_plural "%<PRIuMAX> years ago"
@@ -20966,7 +21564,8 @@
 msgid "invalid --stat value: %s"
 msgstr "無效的 --stat 值:%s"
 
-#: diff.c parse-options.c
+#: diff.c diff.c diff.c diff.c diff.c
+#: parse-options.c parse-options.c
 #, c-format
 msgid "%s expects a numerical value"
 msgstr "%s 期望一個數字值"
@@ -20991,11 +21590,15 @@
 msgstr "ws-error-highlight=%.*s 之後未知的值"
 
 #: diff.c
+msgid "--find-object requires a git repository"
+msgstr "--find-object 需要一個 git 版本庫"
+
+#: diff.c
 #, c-format
 msgid "unable to resolve '%s'"
 msgstr "不能解析 '%s'"
 
-#: diff.c
+#: diff.c diff.c
 #, c-format
 msgid "%s expects <n>/<m> form"
 msgstr "%s 期望 <n>/<m> 格式"
@@ -21015,7 +21618,7 @@
 msgid "invalid mode '%s' in --color-moved-ws"
 msgstr "--color-moved-ws 中的無效模式 '%s'"
 
-#: diff.c
+#: diff.c diff.c
 #, c-format
 msgid "invalid argument to %s"
 msgstr "%s 的參數無效"
@@ -21047,15 +21650,15 @@
 msgid "Diff output format options"
 msgstr "差異輸出格式化選項"
 
-#: diff.c
+#: diff.c diff.c
 msgid "generate patch"
 msgstr "生成修補檔"
 
-#: diff.c
+#: diff.c diff.c diff.c
 msgid "<n>"
 msgstr "<n>"
 
-#: diff.c parse-options.h
+#: diff.c diff.c parse-options.h
 msgid "generate diffs with <n> lines context"
 msgstr "生成含 <n> 行上下文的差異"
 
@@ -21079,7 +21682,7 @@
 msgid "output only the last line of --stat"
 msgstr "只輸出 --stat 的最後一行"
 
-#: diff.c
+#: diff.c diff.c
 msgid "<param1>,<param2>..."
 msgstr "<param1>,<param2>..."
 
@@ -21120,7 +21723,7 @@
 msgid "generate diffstat"
 msgstr "生成差異統計(diffstat)"
 
-#: diff.c
+#: diff.c diff.c diff.c
 msgid "<width>"
 msgstr "<寬度>"
 
@@ -21178,7 +21781,7 @@
 "在 --raw 或者 --numstat 中,不對路徑字元轉檔並使用 NUL 字元做為輸出欄位的分隔"
 "符"
 
-#: diff.c
+#: diff.c diff.c diff.c diff.c
 msgid "<prefix>"
 msgstr "<前綴>"
 
@@ -21206,7 +21809,7 @@
 msgid "show context between diff hunks up to the specified number of lines"
 msgstr "顯示指定行數的差異區塊間的上下文"
 
-#: diff.c
+#: diff.c diff.c diff.c
 msgid "<char>"
 msgstr "<字元>"
 
@@ -21296,7 +21899,7 @@
 msgid "ignore changes whose lines are all blank"
 msgstr "忽略整行都是空白的變更"
 
-#: diff.c
+#: diff.c diff.c diff.c diff.c
 msgid "<regex>"
 msgstr "<正則>"
 
@@ -21324,7 +21927,7 @@
 msgid "generate diff using the \"anchored diff\" algorithm"
 msgstr "使用 \"anchored diff\" 演算法生成差異"
 
-#: diff.c
+#: diff.c diff.c diff.c
 msgid "<mode>"
 msgstr "<模式>"
 
@@ -21432,7 +22035,7 @@
 msgid "control the order in which files appear in the output"
 msgstr "控制輸出中的檔案顯示順序"
 
-#: diff.c
+#: diff.c diff.c
 msgid "<path>"
 msgstr "<路徑>"
 
@@ -21467,10 +22070,6 @@
 msgstr "<depth>"
 
 #: diff.c
-msgid "maximum tree depth to recurse"
-msgstr "樹狀物件的遞迴最大深度"
-
-#: diff.c
 msgid "<file>"
 msgstr "<檔案>"
 
@@ -21511,12 +22110,12 @@
 msgid "pathspec '%s' did not match any file(s) known to git"
 msgstr "路徑規格 '%s' 未符合任何 git 已知檔案"
 
-#: dir.c
+#: dir.c dir.c dir.c dir.c
 #, c-format
 msgid "unrecognized pattern: '%s'"
 msgstr "不認識樣式:「%s」"
 
-#: dir.c
+#: dir.c dir.c
 #, c-format
 msgid "unrecognized negative pattern: '%s'"
 msgstr "不認識反向模式:「%s」"
@@ -21543,7 +22142,7 @@
 msgid "untracked cache is disabled on this system or location"
 msgstr "快取未追蹤檔案在本系統或位置中被停用"
 
-#: dir.c
+#: dir.c dir.c
 msgid ""
 "No directory name could be guessed.\n"
 "Please specify a directory on the command line"
@@ -21556,7 +22155,7 @@
 msgid "index file corrupt in repo %s"
 msgstr "版本庫 %s 中的索引檔案損壞"
 
-#: dir.c
+#: dir.c dir.c
 #, c-format
 msgid "could not create directories for %s"
 msgstr "無法建立 %s 的目錄"
@@ -21571,7 +22170,8 @@
 msgid "hint: Waiting for your editor to close the file...%c"
 msgstr "提示:等待您的編輯器關閉檔案...%c"
 
-#: editor.c sequencer.c wrapper.c
+#: editor.c sequencer.c sequencer.c sequencer.c
+#: sequencer.c sequencer.c wrapper.c
 #, c-format
 msgid "could not write to '%s'"
 msgstr "無法寫入「%s」"
@@ -21610,7 +22210,7 @@
 msgid "abbrev length out of range: %d"
 msgstr "縮寫長度超出範圍:%d"
 
-#: environment.c
+#: environment.c environment.c
 #, c-format
 msgid "bad zlib compression level %d"
 msgstr "錯誤的 zlib 壓縮級別 %d"
@@ -21700,27 +22300,27 @@
 msgid "Server supports filter"
 msgstr "伺服器支援 filter"
 
-#: fetch-pack.c
+#: fetch-pack.c fetch-pack.c
 #, c-format
 msgid "invalid shallow line: %s"
 msgstr "無效的 shallow 訊息:%s"
 
-#: fetch-pack.c
+#: fetch-pack.c fetch-pack.c
 #, c-format
 msgid "invalid unshallow line: %s"
 msgstr "無效的 unshallow 訊息:%s"
 
-#: fetch-pack.c
+#: fetch-pack.c fetch-pack.c
 #, c-format
 msgid "error in object: %s"
 msgstr "物件中發生錯誤:%s"
 
-#: fetch-pack.c
+#: fetch-pack.c fetch-pack.c
 #, c-format
 msgid "no shallow found: %s"
 msgstr "未發現 shallow:%s"
 
-#: fetch-pack.c
+#: fetch-pack.c fetch-pack.c
 #, c-format
 msgid "expected shallow/unshallow, got %s"
 msgstr "應為 shallow/unshallow,卻得到 %s"
@@ -21789,7 +22389,10 @@
 msgid "Server version is %.*s"
 msgstr "伺服器版本 %.*s"
 
-#: fetch-pack.c
+#: fetch-pack.c fetch-pack.c fetch-pack.c fetch-pack.c
+#: fetch-pack.c fetch-pack.c fetch-pack.c fetch-pack.c
+#: fetch-pack.c fetch-pack.c fetch-pack.c fetch-pack.c
+#: fetch-pack.c fetch-pack.c fetch-pack.c fetch-pack.c
 #, c-format
 msgid "Server supports %s"
 msgstr "伺服器支援 %s"
@@ -21818,7 +22421,7 @@
 msgid "no common commits"
 msgstr "沒有共同的提交"
 
-#: fetch-pack.c
+#: fetch-pack.c fetch-pack.c
 msgid "git fetch-pack: fetch failed."
 msgstr "git fetch-pack:取得失敗。"
 
@@ -21836,7 +22439,7 @@
 msgid "Server does not support shallow requests"
 msgstr "伺服器不支援 shallow 請求"
 
-#: fetch-pack.c
+#: fetch-pack.c fetch-pack.c
 msgid "unable to write request to remote"
 msgstr "無法將請求寫到遠端"
 
@@ -21901,6 +22504,11 @@
 msgstr "git fetch-pack: 預期要有回應結束封包"
 
 #: fetch-pack.c
+#, c-format
+msgid "couldn't resolve 'auto' filter '%s': %s"
+msgstr "無法解析「auto」過濾器「%s」:%s"
+
+#: fetch-pack.c
 msgid "no matching remote head"
 msgstr "沒有符合的遠端分支"
 
@@ -22001,7 +22609,7 @@
 msgid "unsupported command listing type '%s'"
 msgstr "不支援的命令列表類型 '%s'"
 
-#: git.c
+#: git.c git.c git.c
 #, c-format
 msgid "no directory given for '%s' option\n"
 msgstr "未傳入目錄至「%s」選項\n"
@@ -22096,11 +22704,12 @@
 msgid "failed to run command '%s': %s\n"
 msgstr "執行指令 '%s' 失敗:%s\n"
 
+#: gpg-interface.c gpg-interface.c gpg-interface.c
 #: gpg-interface.c
 msgid "could not create temporary file"
 msgstr "無法建立暫存檔"
 
-#: gpg-interface.c
+#: gpg-interface.c gpg-interface.c
 #, c-format
 msgid "failed writing detached signature to '%s'"
 msgstr "無法將分離式簽名寫入 '%s'"
@@ -22134,7 +22743,7 @@
 msgid "failed to get the ssh fingerprint for key '%s'"
 msgstr "無法取得「%s」金鑰的 SSH 指紋"
 
-#: gpg-interface.c
+#: gpg-interface.c gpg-interface.c
 #, c-format
 msgid "failed to get the ssh fingerprint for key %s"
 msgstr "無法取得 %s 金鑰的 SSH 指紋"
@@ -22319,10 +22928,10 @@
 "'%s' appears to be a git command, but we were not\n"
 "able to execute it. Maybe git-%s is broken?"
 msgstr ""
-"'%s' 像是一個 git 指令,但卻無法執行。\n"
+"「%s」似乎是 git 命令,但卻無法執行。\n"
 "可能是 git-%s 受損?"
 
-#: help.c
+#: help.c help.c
 #, c-format
 msgid "git: '%s' is not a git command. See 'git --help'."
 msgstr "git:'%s' 不是一個 git 指令。參見 'git --help'。"
@@ -22391,7 +23000,19 @@
 "因為沒有將掛鉤「%s」設定為可執行,因此忽略這個掛鉤。\n"
 "您可以透過設定「git config set advice.ignoredHook false」來關閉這則警告。"
 
-#: http-fetch.c
+#: hook.c
+#, c-format
+msgid "disabled hook '%s' has no command configured"
+msgstr "停用的掛鉤「%s」沒有設定命令"
+
+#: hook.c
+#, c-format
+msgid ""
+"'hook.%s.command' must be configured or 'hook.%s.event' must be removed; "
+"aborting."
+msgstr "必須設定「hook.%s.command」或移除「hook.%s.event」,中止。"
+
+#: http-fetch.c http-fetch.c
 msgid "not a git repository"
 msgstr "不是一個 git 版本庫"
 
@@ -22413,7 +23034,7 @@
 msgid "Unknown value for http.proactiveauth"
 msgstr "http.proactiveauth 的值未知"
 
-#: http.c parse.c
+#: http.c http.c parse.c
 #, c-format
 msgid "failed to parse %s"
 msgstr "解析 %s 失敗"
@@ -22452,6 +23073,23 @@
 "     請求:%s\n"
 "   重定向:%s"
 
+#: http.c
+#, c-format
+msgid ""
+"response requested a delay greater than http.maxRetryTime (%ld > %ld seconds)"
+msgstr "回應要求延遲的時間大於 http.maxRetryTime(%ld > %ld 秒)"
+
+#: http.c
+#, c-format
+msgid ""
+"configured http.retryAfter exceeds http.maxRetryTime (%ld > %ld seconds)"
+msgstr "已設定的 http.retryAfter 超過 http.maxRetryTime(%ld > %ld 秒)"
+
+#: http.c
+#, c-format
+msgid "rate limited, waiting %ld seconds before retry"
+msgstr "受到速率限制,等待 %ld 秒後重試"
+
 #: http.h
 #, c-format
 msgid ""
@@ -22550,6 +23188,10 @@
 "(比如「git config imap.folder Drafts」)"
 
 #: list-objects-filter-options.c
+msgid "'auto' filter not supported by this command"
+msgstr "此命令不支援「auto」過濾器"
+
+#: list-objects-filter-options.c
 msgid "expected 'tree:<depth>'"
 msgstr "期望 'tree:<深度>'"
 
@@ -22573,6 +23215,10 @@
 msgstr "必須對 sub-filter-spec 中的字元進行轉義:'%c'"
 
 #: list-objects-filter-options.c
+msgid "an 'auto' filter cannot be combined"
+msgstr "「auto」過濾器無法與其他過濾器結合使用"
+
+#: list-objects-filter-options.c
 msgid "expected something after combine:"
 msgstr "期望在組合後有一些東西:"
 
@@ -22580,6 +23226,10 @@
 msgid "multiple filter-specs cannot be combined"
 msgstr "不能混用多種過濾規格"
 
+#: list-objects-filter-options.c list-objects-filter-options.c
+msgid "an 'auto' filter is incompatible with any other filter"
+msgstr "「auto」過濾器與其他過濾器均不相容"
+
 #: list-objects-filter-options.c
 msgid "unable to upgrade repository format to support partial clone"
 msgstr "無法升級版本庫格式,以支援部分拓製"
@@ -22619,22 +23269,46 @@
 
 #: lockfile.c
 #, c-format
+msgid "could not write lock pid file '%s'"
+msgstr "無法寫入鎖定 PID 檔案「%s」"
+
+#: lockfile.c
+#, c-format
+msgid "malformed lock pid file '%s'"
+msgstr "鎖定 PID 檔案「%s」有格式錯誤"
+
+#: lockfile.c
+#, c-format
 msgid ""
-"Unable to create '%s.lock': %s.\n"
+"Unable to create '%s': %s.\n"
 "\n"
-"Another git process seems to be running in this repository, e.g.\n"
-"an editor opened by 'git commit'. Please make sure all processes\n"
-"are terminated then try again. If it still fails, a git process\n"
-"may have crashed in this repository earlier:\n"
-"remove the file manually to continue."
 msgstr ""
-"無法建立 '%s.lock':%s。\n"
+"無法建立「%s」:%s。\n"
 "\n"
-"這個版本庫似乎有另一個 git 處理程序在執行,\n"
-"例如「git commit」命令開啟的編輯器。\n"
-"請確認所有處理程序都已經終止後再重試一次。如果錯誤沒有消失,\n"
-"有可能是之前在這個版本庫執行的 git 處理程序當掉了。\n"
-"如果是這樣,請自行刪除這個檔案再繼續。"
+
+#: lockfile.c
+#, c-format
+msgid ""
+"Lock may be held by process %<PRIuMAX>; if no git process is running, the "
+"lock file may be stale (PIDs can be reused)"
+msgstr ""
+"鎖可能被處理程序 %<PRIuMAX> 持有;若沒有 git 處理程序在執行,這個鎖定檔可能已"
+"經過期(PID 可能會被重複使用)"
+
+#: lockfile.c
+#, c-format
+msgid ""
+"Lock was held by process %<PRIuMAX>, which is no longer running; the lock "
+"file appears to be stale"
+msgstr ""
+"鎖先前由處理程序 %<PRIuMAX> 持有,但這個處理程序已不再執行;鎖定檔似乎已經過"
+"期"
+
+#: lockfile.c
+msgid ""
+"Another git process seems to be running in this repository, or the lock file "
+"may be stale"
+msgstr "似乎有另一個 git 處理程序正在此版本庫中執行,或者鎖定檔案可能已經過期"
 
 #: lockfile.c
 #, c-format
@@ -22650,7 +23324,7 @@
 msgid "could not write loose object index %s"
 msgstr "無法寫入鬆散物件索引 %s"
 
-#: loose.c
+#: loose.c loose.c
 #, c-format
 msgid "failed to write loose object index %s"
 msgstr "寫入鬆散物件索引 %s 失敗"
@@ -22668,12 +23342,12 @@
 msgid "quoted CRLF detected"
 msgstr "偵測到由可列印字元 (quoted) 所組成的 CRLF"
 
-#: mem-pool.c strbuf.c wrapper.c
+#: mem-pool.c strbuf.c strbuf.c wrapper.c
 #, c-format
 msgid "unable to format message: %s"
 msgstr "無法格式化訊息:%s"
 
-#: merge-ll.c
+#: merge-ll.c merge-ll.c
 #, c-format
 msgid "invalid marker-size '%s', expecting an integer"
 msgstr "無效的 marker-size「%s」,應為整數"
@@ -22698,6 +23372,7 @@
 msgid "Failed to merge submodule %s (commits not present)"
 msgstr "無法合併子模組 %s(提交不存在)"
 
+#: merge-ort.c merge-ort.c merge-ort.c merge-ort.c
 #: merge-ort.c
 #, c-format
 msgid "error: failed to merge submodule %s (repository corrupt)"
@@ -22708,7 +23383,7 @@
 msgid "Failed to merge submodule %s (commits don't follow merge-base)"
 msgstr "無法合併子模組 %s (提交未跟隨合併基礎)"
 
-#: merge-ort.c
+#: merge-ort.c merge-ort.c
 #, c-format
 msgid "Note: Fast-forwarding submodule %s to %s"
 msgstr "注意:正在將 %s 子模組快轉到 %s"
@@ -22835,7 +23510,7 @@
 "衝突(重新命名陷入相撞):%s -> %s 這個重新命名有內容衝突並與其他路徑相撞,可"
 "能會因此出現巢狀衝突標記。"
 
-#: merge-ort.c
+#: merge-ort.c merge-ort.c
 #, c-format
 msgid "CONFLICT (rename/delete): %s renamed to %s in %s, but deleted in %s."
 msgstr ""
@@ -22984,8 +23659,17 @@
 msgstr "橫列格式錯誤:%s"
 
 #: midx-write.c
-msgid "could not load pack"
-msgstr "無法載入包"
+#, c-format
+msgid "could not load pack %d"
+msgstr "無法載入封裝 %d"
+
+#: midx-write.c
+msgid "too many packs, unable to compact"
+msgstr "過多封裝,無法壓縮"
+
+#: midx-write.c pack-revindex.c
+msgid "could not determine preferred pack"
+msgstr "無法確定偏好封裝"
 
 #: midx-write.c
 #, c-format
@@ -22998,6 +23682,15 @@
 msgstr "清理位於 %s 的多包索引失敗"
 
 #: midx-write.c
+#, c-format
+msgid "unknown MIDX version: %d"
+msgstr "未知的 MIDX 版本:%d"
+
+#: midx-write.c
+msgid "cannot perform MIDX compaction with v1 format"
+msgstr "無法在 v1 格式下執行 MIDX 壓縮"
+
+#: midx-write.c
 msgid "ignoring existing multi-pack-index; checksum mismatch"
 msgstr "忽略現有的多包索引:總和檢查碼不符"
 
@@ -23314,12 +24007,14 @@
 msgid "failed to map tree entry for %s"
 msgstr "無法為 %s 映射樹狀物件項目"
 
+#: object-file-convert.c object-file-convert.c
+#: object-file-convert.c object-file-convert.c
 #: object-file-convert.c
 #, c-format
 msgid "bad %s in commit"
 msgstr "提交中有無效的 %s"
 
-#: object-file-convert.c
+#: object-file-convert.c object-file-convert.c
 #, c-format
 msgid "unable to map %s %s in commit object"
 msgstr "無法在提交物件中映射 %s %s"
@@ -23329,17 +24024,17 @@
 msgid "Failed to convert object from %s to %s"
 msgstr "無法將物件從 %s 轉換為 %s"
 
-#: object-file.c
+#: object-file.c object-file.c
 #, c-format
 msgid "object file %s is empty"
 msgstr "物件檔案 %s 為空"
 
-#: object-file.c
+#: object-file.c object-file.c
 #, c-format
 msgid "corrupt loose object '%s'"
 msgstr "損壞的鬆散物件 '%s'"
 
-#: object-file.c
+#: object-file.c object-file.c
 #, c-format
 msgid "garbage at end of loose object '%s'"
 msgstr "鬆散物件 '%s' 後面有垃圾資料"
@@ -23373,7 +24068,7 @@
 msgid "loose object %s (stored in %s) is corrupt"
 msgstr "鬆散物件 %s(儲存在 %s)已損壞"
 
-#: object-file.c
+#: object-file.c object-file.c object-file.c
 #, c-format
 msgid "unable to open %s"
 msgstr "不能開啟 %s"
@@ -23478,7 +24173,7 @@
 msgid "short read while indexing %s"
 msgstr "索引 %s 時讀入不完整"
 
-#: object-file.c
+#: object-file.c object-file.c
 #, c-format
 msgid "%s: failed to insert into database"
 msgstr "%s:插入資料庫失敗"
@@ -23702,11 +24397,6 @@
 
 #: object.c
 #, c-format
-msgid "object %s is a %s, not a %s"
-msgstr "物件 %s 是一個 %s,不是一個 %s"
-
-#: object.c
-#, c-format
 msgid "object %s has unknown type id %d"
 msgstr "物件 %s 有未知的類型 id %d"
 
@@ -23717,6 +24407,11 @@
 
 #: object.c
 #, c-format
+msgid "unable to open object stream for %s"
+msgstr "無法開啟 %s 的物件串流"
+
+#: object.c object.c
+#, c-format
 msgid "hash mismatch %s"
 msgstr "雜湊值與 %s 不符合"
 
@@ -23736,18 +24431,6 @@
 msgstr "%s:忽略備用物件庫,嵌套太深"
 
 #: odb.c
-msgid "unable to fdopen alternates lockfile"
-msgstr "無法 fdopen 取代鎖檔案"
-
-#: odb.c
-msgid "unable to read alternates file"
-msgstr "無法讀取替代檔案"
-
-#: odb.c
-msgid "unable to move new alternates file into place"
-msgstr "無法將新的替代檔案移動到位"
-
-#: odb.c
 #, c-format
 msgid "path '%s' does not exist"
 msgstr "路徑 '%s' 不存在"
@@ -23792,7 +24475,7 @@
 msgid "packed object %s (stored in %s) is corrupt"
 msgstr "打包物件 %s(儲存在 %s)已損壞"
 
-#: odb.c
+#: odb.c odb.c
 #, c-format
 msgid "missing mapping of %s to %s"
 msgstr "缺少 %s 到 %s 的映射"
@@ -23802,6 +24485,18 @@
 msgid "%s is not a valid '%s' object"
 msgstr "%s 不是一個有效的 '%s' 物件"
 
+#: odb/source-files.c
+msgid "unable to fdopen alternates lockfile"
+msgstr "無法 fdopen 取代鎖檔案"
+
+#: odb/source-files.c
+msgid "unable to read alternates file"
+msgstr "無法讀取替代檔案"
+
+#: odb/source-files.c
+msgid "unable to move new alternates file into place"
+msgstr "無法將新的替代檔案移動到位"
+
 #: pack-bitmap-write.c
 #, c-format
 msgid "duplicate entry when writing bitmap index: %s"
@@ -23877,7 +24572,7 @@
 msgid "invalid XOR offset in bitmap pack index"
 msgstr "位圖包索引的 XOR 偏移無效"
 
-#: pack-bitmap.c
+#: pack-bitmap.c pack-bitmap.c
 msgid "cannot fstat bitmap file"
 msgstr "無法 fstat 位圖檔案"
 
@@ -23907,7 +24602,7 @@
 msgid "corrupt bitmap lookup table: commit index %u out of range"
 msgstr "位圖查詢表損壞:提交索引 %u 超出範圍"
 
-#: pack-bitmap.c
+#: pack-bitmap.c pack-bitmap.c
 #, c-format
 msgid "corrupt ewah bitmap: truncated header for bitmap of commit \"%s\""
 msgstr "ewah 位圖損壞:提交「%s」之位圖的標頭遭截斷"
@@ -23936,11 +24631,12 @@
 msgid "object '%s': real type '%s', expected: '%s'"
 msgstr "「%s」物件:實際類型是「%s」,但預期是「%s」"
 
-#: pack-bitmap.c
+#: pack-bitmap.c pack-bitmap.c
 #, c-format
 msgid "object not in bitmap: '%s'"
 msgstr "物件不在位圖中:「%s」"
 
+#: pack-bitmap.c pack-bitmap.c pack-bitmap.c pack-bitmap.c
 #: pack-bitmap.c
 msgid "failed to load bitmap indexes"
 msgstr "無法載入位圖索引"
@@ -23958,7 +24654,7 @@
 msgid "mismatch in bitmap results"
 msgstr "位圖結果中有不符項目"
 
-#: pack-bitmap.c
+#: pack-bitmap.c pack-bitmap.c
 #, c-format
 msgid "pseudo-merge index out of range (%<PRIu32> >= %<PRIuMAX>)"
 msgstr "偽合併索引超出範圍(%<PRIu32> >= %<PRIuMAX>)"
@@ -24061,10 +24757,6 @@
 msgid "multi-pack-index reverse-index chunk is the wrong size"
 msgstr "多包索引的反向索引區塊大小有誤"
 
-#: pack-revindex.c
-msgid "could not determine preferred pack"
-msgstr "無法確定偏好封裝"
-
 #: pack-write.c
 msgid "cannot both write and verify reverse index"
 msgstr "無法同時寫入和驗證倒排索引"
@@ -24074,7 +24766,7 @@
 msgid "could not stat: %s"
 msgstr "無法 stat:%s"
 
-#: pack-write.c
+#: pack-write.c pack-write.c
 #, c-format
 msgid "failed to make %s readable"
 msgstr "無法讓 %s 能夠寫入"
@@ -24095,6 +24787,11 @@
 
 #: packfile.c
 #, c-format
+msgid "could not load .mtimes for cruft pack '%s'"
+msgstr "無法載入廢棄封裝「%s」的 .mtimes"
+
+#: packfile.c
+#, c-format
 msgid "offset before start of pack index for %s (corrupt index?)"
 msgstr "位移量在 %s 的包索引開始之前(損壞的索引?)"
 
@@ -24113,7 +24810,7 @@
 msgid "option `%s' expects \"always\", \"auto\", or \"never\""
 msgstr "選項 `%s' 期望 \"always\"、\"auto\" 或 \"never\""
 
-#: parse-options-cb.c
+#: parse-options-cb.c parse-options-cb.c
 #, c-format
 msgid "malformed object name '%s'"
 msgstr "格式錯誤的物件名 '%s'"
@@ -24128,7 +24825,7 @@
 msgid "%s requires a value"
 msgstr "%s 需要一個值"
 
-#: parse-options.c
+#: parse-options.c parse-options.c
 #, c-format
 msgid "%s takes no value"
 msgstr "%s 不取值"
@@ -24143,7 +24840,7 @@
 msgid "value for %s exceeds %<PRIdMAX>"
 msgstr "%s 的值超過 %<PRIdMAX>"
 
-#: parse-options.c
+#: parse-options.c parse-options.c parse-options.c
 #, c-format
 msgid "value %s for %s not in range [%<PRIdMAX>,%<PRIdMAX>]"
 msgstr "%2$s 的數值 %1$s 不在 [%3$<PRIdMAX>,%4$<PRIdMAX>] 範圍內"
@@ -24163,12 +24860,12 @@
 msgid "ambiguous option: %s (could be --%s%s or --%s%s)"
 msgstr "有歧義的選項:%s(可以是 --%s%s 或 --%s%s)"
 
-#: parse-options.c
+#: parse-options.c parse-options.c
 #, c-format
 msgid "did you mean `--%s` (with two dashes)?"
 msgstr "你的意思是 `--%s`(有兩個短線)嗎?"
 
-#: parse-options.c
+#: parse-options.c parse-options.c
 #, c-format
 msgid "alias of --%s"
 msgstr "--%s 的別名"
@@ -24301,7 +24998,7 @@
 msgid "expiry-date"
 msgstr "到期時間"
 
-#: parse-options.h
+#: parse-options.h parse-options.h
 msgid "no-op (backward compatibility)"
 msgstr "空動作(向後相容)"
 
@@ -24464,11 +25161,11 @@
 msgid "protocol error: impossibly long line"
 msgstr "協定錯誤:不可能的長行"
 
-#: pkt-line.c
+#: pkt-line.c pkt-line.c
 msgid "packet write with format failed"
 msgstr "格式化包寫入錯誤"
 
-#: pkt-line.c
+#: pkt-line.c pkt-line.c
 msgid "packet write failed - data exceeds max packet size"
 msgstr "寫封包失敗:資料超過了包的最大長度"
 
@@ -24477,20 +25174,20 @@
 msgid "packet write failed: %s"
 msgstr "封包寫入失敗:%s"
 
-#: pkt-line.c
+#: pkt-line.c pkt-line.c
 msgid "read error"
 msgstr "讀取錯誤"
 
-#: pkt-line.c
+#: pkt-line.c pkt-line.c
 msgid "the remote end hung up unexpectedly"
 msgstr "遠端意外掛斷了"
 
-#: pkt-line.c
+#: pkt-line.c pkt-line.c
 #, c-format
 msgid "protocol error: bad line length character: %.4s"
 msgstr "協定錯誤:錯誤的行長度字串:%.4s"
 
-#: pkt-line.c
+#: pkt-line.c pkt-line.c pkt-line.c pkt-line.c
 #, c-format
 msgid "protocol error: bad line length %d"
 msgstr "協定錯誤:錯誤的行長度 %d"
@@ -24513,6 +25210,11 @@
 msgid "unable to parse --pretty format"
 msgstr "不能解析 --pretty 格式"
 
+#: pretty.c pretty.c
+#, c-format
+msgid "%s is not supported by this command"
+msgstr "此命令不支援 %s"
+
 #: promisor-remote.c
 msgid "lazy fetching disabled; some objects may not be available"
 msgstr "延後抓取已被停用;某些物件可能無法使用"
@@ -24521,7 +25223,7 @@
 msgid "promisor-remote: unable to fork off fetch subprocess"
 msgstr "promisor-remote: 無法 fork fetch 子處理程序"
 
-#: promisor-remote.c
+#: promisor-remote.c promisor-remote.c
 msgid "promisor-remote: could not write to fetch subprocess"
 msgstr "promisor-remote: 無法寫入 fetch 子處理程序"
 
@@ -24566,6 +25268,25 @@
 
 #: promisor-remote.c
 #, c-format
+msgid ""
+"Storing new %s from server for remote '%s'.\n"
+"    '%s' -> '%s'\n"
+msgstr ""
+"正在為遠端「%2$s」從伺服器儲存新的 %1$s。\n"
+"    '%3$s' -> '%4$s'\n"
+
+#: promisor-remote.c
+#, c-format
+msgid "invalid filter '%s' for remote '%s' will not be stored: %s"
+msgstr "遠端「%2$s」的無效篩選器「%1$s」將不儲存:%3$s"
+
+#: promisor-remote.c
+#, c-format
+msgid "invalid token '%s' for remote '%s' will not be stored"
+msgstr "遠端「%2$s」的無效權杖「%1$s」將不儲存"
+
+#: promisor-remote.c
+#, c-format
 msgid "unknown '%s' value for '%s' config option"
 msgstr "「%2$s」組態選項的值「%1$s」未知"
 
@@ -24574,6 +25295,11 @@
 msgid "accepted promisor remote '%s' not found"
 msgstr "找不到接受的承諾者遠端「%s」"
 
+#: promisor-remote.c
+#, c-format
+msgid "promisor remote '%s' advertised invalid filter '%s': %s"
+msgstr "承諾者遠端「%s」宣告了無效的篩選器「%s」:%s"
+
 #: protocol-caps.c
 msgid "object-info: expected flush after arguments"
 msgstr "object-info:引數後預期要有 flush"
@@ -24587,7 +25313,7 @@
 msgid "failed to load pseudo-merge regex for %s: '%s'"
 msgstr "未能載入 %s 的偽合併常規表示式:%s"
 
-#: pseudo-merge.c
+#: pseudo-merge.c pseudo-merge.c
 #, c-format
 msgid "%s must be non-negative, using default"
 msgstr "%s 須為非負數,將採用預設值"
@@ -24687,7 +25413,7 @@
 "range-diff:無法計算範圍差異,因為它超過了成本矩陣的最大記憶體限制:%s(需要 "
 "%<PRIuMAX> 位元組),限制為 %s(%<PRIuMAX> 位元組)"
 
-#: range-diff.c
+#: range-diff.c range-diff.c
 #, c-format
 msgid "could not parse log for '%s'"
 msgstr "無法解析「%s」的日誌"
@@ -24728,7 +25454,7 @@
 #: read-cache.c
 #, c-format
 msgid "'%s' appears as both a file and as a directory"
-msgstr "'%s' 看起來既是檔案又是目錄"
+msgstr "「%s」看起來既是檔案又是目錄"
 
 #: read-cache.c
 msgid "Refresh index"
@@ -24881,6 +25607,15 @@
 
 #: read-cache.c
 #, c-format
+msgid ""
+"Skipping submodule due to ignore=all: %s\n"
+"Use --force if you really want to add the submodule."
+msgstr ""
+"因 ignore=all 而跳過子模組:%s\n"
+"如果您確實想要加入該子模組,請使用 --force。"
+
+#: read-cache.c
+#, c-format
 msgid "unexpected diff status %c"
 msgstr "非預期的 diff 狀態 %c"
 
@@ -24934,7 +25669,7 @@
 "\n"
 "命令:\n"
 "p, pick <提交> = 使用提交\n"
-"r, reword <提交> = 使用提交,但編輯提交說明\n"
+"r, reword <提交> = 使用提交,但改寫提交說明\n"
 "e, edit <提交> = 使用提交,但不直接修補 (amend) \n"
 "s, squash <提交> = 使用提交,但融合至上個提交\n"
 "f, fixup [-C | -c] <提交> = 跟「squash」相似,但除非傳入 -C,\n"
@@ -25093,7 +25828,7 @@
 #: ref-filter.c
 #, c-format
 msgid "unknown %%(trailers) argument: %s"
-msgstr "未知的 %%(trailers) 參數:%s"
+msgstr "未知的 %%(trailers) 引數:%s"
 
 #: ref-filter.c
 #, c-format
@@ -25115,7 +25850,7 @@
 msgid "cannot fully parse %s=%s"
 msgstr "無法完全解析 %s=%s"
 
-#: ref-filter.c
+#: ref-filter.c ref-filter.c
 #, c-format
 msgid "value expected %s="
 msgstr "數值預期 %s="
@@ -25176,7 +25911,7 @@
 "not a git repository, but the field '%.*s' requires access to object data"
 msgstr "不是一個 git 版本庫,但是欄位 '%.*s' 需要存取物件資料"
 
-#: ref-filter.c
+#: ref-filter.c ref-filter.c ref-filter.c ref-filter.c
 #, c-format
 msgid "format: %%(%s) atom used without a %%(%s) atom"
 msgstr "format:使用 %%(%s) 元素卻缺少 %%(%s) 元素"
@@ -25254,7 +25989,7 @@
 msgid "(no branch)"
 msgstr "(無分支)"
 
-#: ref-filter.c
+#: ref-filter.c ref-filter.c
 #, c-format
 msgid "missing object %s for %s"
 msgstr "缺少 %2$s 的物件 %1$s"
@@ -25312,6 +26047,11 @@
 msgstr "沒有 '%s' 的引用日誌"
 
 #: refs.c
+#, c-format
+msgid "in '%s' phase, update aborted by the reference-transaction hook"
+msgstr "在「%s」階段中,reference-transaction 掛鉤中止了更新"
+
+#: refs.c
 msgid "Checking references consistency"
 msgstr "正在檢查引用一致性"
 
@@ -25441,21 +26181,17 @@
 msgid "ref updates forbidden inside quarantine environment"
 msgstr "在隔離環境中禁止更新引用"
 
-#: refs.c
-msgid "ref updates aborted by hook"
-msgstr "引用更新被掛鉤拒絕"
-
-#: refs.c
+#: refs.c refs.c
 #, c-format
 msgid "'%s' exists; cannot create '%s'"
 msgstr "'%s' 已存在,無法建立 '%s'"
 
-#: refs.c
+#: refs.c refs.c
 #, c-format
 msgid "cannot process '%s' and '%s' at the same time"
 msgstr "無法同時處理 '%s' 和 '%s'"
 
-#: refs.c
+#: refs.c refs.c
 #, c-format
 msgid "could not delete reference %s: %s"
 msgstr "無法刪除引用 %s:%s"
@@ -25676,6 +26412,16 @@
 
 #: remote-curl.c
 #, c-format
+msgid "rate limited by '%s', please try again in %ld seconds"
+msgstr "受到「%s」的速率限制,請在 %ld 秒後再試"
+
+#: remote-curl.c
+#, c-format
+msgid "rate limited by '%s', please try again later"
+msgstr "受到「%s」的速率限制,請稍後再試"
+
+#: remote-curl.c
+#, c-format
 msgid "unable to access '%s': %s"
 msgstr "無法存取 '%s':%s"
 
@@ -25742,12 +26488,12 @@
 msgid "cannot fetch by sha1 over smart http"
 msgstr "無法透過智慧 HTTP 取得 sha1"
 
-#: remote-curl.c
+#: remote-curl.c remote-curl.c
 #, c-format
 msgid "protocol error: expected sha/ref, got '%s'"
 msgstr "協定錯誤:期望 sha/ref,卻得到 '%s'"
 
-#: remote-curl.c
+#: remote-curl.c remote-curl.c
 #, c-format
 msgid "http transport does not support %s"
 msgstr "http 傳輸協定不支援 %s"
@@ -25827,7 +26573,7 @@
 msgid "unrecognized value transfer.credentialsInUrl: '%s'"
 msgstr "數值 transfer.credentialsInUrl 不認識:「%s」"
 
-#: remote.c
+#: remote.c remote.c
 #, c-format
 msgid "URL '%s' uses plaintext credentials"
 msgstr "URL「%s」使用明文憑證"
@@ -25953,7 +26699,7 @@
 msgid "dst ref %s receives from more than one src"
 msgstr "目的地引用 %s 接收超過一個來源"
 
-#: remote.c
+#: remote.c remote.c
 msgid "HEAD does not point to a branch"
 msgstr "HEAD 沒有指向一個分支"
 
@@ -26007,12 +26753,10 @@
 
 #: remote.c
 #, c-format
-msgid "Your branch is based on '%s', but the upstream is gone.\n"
-msgstr "您的分支基於 '%s',但此上游分支已經不存在。\n"
-
-#: remote.c
-msgid "  (use \"git branch --unset-upstream\" to fixup)\n"
-msgstr "  (使用 \"git branch --unset-upstream\" 來修復)\n"
+msgid ""
+"ignoring value '%s' for status.compareBranches, only @{upstream} and @{push} "
+"are supported"
+msgstr "忽略 status.compareBranches 的值「%s」,僅支援 @{upstream} 和 @{push}"
 
 #: remote.c
 #, c-format
@@ -26071,6 +26815,15 @@
 
 #: remote.c
 #, c-format
+msgid "Your branch is based on '%s', but the upstream is gone.\n"
+msgstr "您的分支基於 '%s',但此上游分支已經不存在。\n"
+
+#: remote.c
+msgid "  (use \"git branch --unset-upstream\" to fixup)\n"
+msgstr "  (使用 \"git branch --unset-upstream\" 來修復)\n"
+
+#: remote.c
+#, c-format
 msgid "cannot parse expected object name '%s'"
 msgstr "無法解析期望的物件名 '%s'"
 
@@ -26089,7 +26842,7 @@
 msgid "pack %s too large to consider in geometric progression"
 msgstr "%s 包太大,以致不能在等比數列中考慮"
 
-#: repack-geometry.c
+#: repack-geometry.c repack-geometry.c repack-geometry.c
 #, c-format
 msgid "pack %s too large to roll up"
 msgstr "%s 包太大,以致不能縮合"
@@ -26163,11 +26916,49 @@
 msgid "replace depth too high for object %s"
 msgstr "物件 %s 的取代層級太深"
 
-#: rerere.c
+#: replay.c
+#, c-format
+msgid "'%s' is not a valid commit-ish for %s"
+msgstr "「%s」不是 %s 有效的提交指示元"
+
+#: replay.c
+#, c-format
+msgid "argument to %s must be a reference"
+msgstr "傳入 %s 的引數必須是引用"
+
+#: replay.c
+#, c-format
+msgid ""
+"'%s' cannot be used with multiple revision ranges because the ordering would "
+"be ill-defined"
+msgstr "「%s」不能與多個修訂版範圍一起使用,因為順序無法明確定義"
+
+#: replay.c
+msgid "need some commits to replay"
+msgstr "需要一些提交才能重放"
+
+#: replay.c
+msgid "all positive revisions given must be references"
+msgstr "提供的所有正向修訂集必須為引用"
+
+#: replay.c
+msgid "'--ref' cannot be used with multiple revision ranges"
+msgstr "「--ref」不能與多個修訂版本範圍一起使用"
+
+#: replay.c sequencer.c
+#, c-format
+msgid "'%s' is not a valid refname"
+msgstr "「%s」不是有效的引用名稱"
+
+#: repository.c
+msgid "compatibility hash algorithm support requires Rust"
+msgstr "相容性雜湊演算法支援需要 Rust"
+
+#: rerere.c rerere.c rerere.c
 msgid "corrupt MERGE_RR"
 msgstr "損壞的 MERGE_RR"
 
-#: rerere.c
+#: rerere.c rerere.c
 msgid "unable to write rerere record"
 msgstr "無法寫入 rerere 記錄"
 
@@ -26176,7 +26967,7 @@
 msgid "there were errors while writing '%s' (%s)"
 msgstr "寫入 '%s' (%s) 時發生錯誤"
 
-#: rerere.c
+#: rerere.c rerere.c
 #, c-format
 msgid "could not parse conflict hunks in '%s'"
 msgstr "無法解析「%s」中的衝突區塊"
@@ -26221,7 +27012,7 @@
 msgid "failed to update conflicted state in '%s'"
 msgstr "更新 '%s' 中的衝突狀態失敗"
 
-#: rerere.c
+#: rerere.c rerere.c
 #, c-format
 msgid "no remembered resolution for '%s'"
 msgstr "沒有為 '%s' 記憶的解決方案"
@@ -26248,7 +27039,7 @@
 msgid "could not determine HEAD revision"
 msgstr "無法確定 HEAD 修訂版"
 
-#: reset.c sequencer.c
+#: reset.c reset.c sequencer.c
 #, c-format
 msgid "failed to find tree of %s"
 msgstr "無法找到 %s 指向的樹"
@@ -26296,7 +27087,7 @@
 
 #: revision.c
 msgid "your current branch appears to be broken"
-msgstr "您的目前分支好像被損壞"
+msgstr "您的目前分支似乎損壞了"
 
 #: revision.c
 #, c-format
@@ -26321,7 +27112,7 @@
 msgid "'%s' does not exist"
 msgstr "'%s' 不存在"
 
-#: scalar.c
+#: scalar.c scalar.c scalar.c scalar.c scalar.c
 #, c-format
 msgid "could not switch to '%s'"
 msgstr "無法切換至「%s」"
@@ -26330,11 +27121,11 @@
 msgid "need a working directory"
 msgstr "需要工作目錄"
 
-#: scalar.c
+#: scalar.c scalar.c
 msgid "Scalar enlistments require a worktree"
 msgstr "純量編列名單需要工作目錄"
 
-#: scalar.c
+#: scalar.c scalar.c
 #, c-format
 msgid "could not configure %s=%s"
 msgstr "無法設定 %s=%s"
@@ -26412,7 +27203,7 @@
 msgid "specify if tags should be fetched during clone"
 msgstr "指定是否要在拓製階段抓取標籤"
 
-#: scalar.c
+#: scalar.c scalar.c
 msgid "specify if background maintenance should be enabled"
 msgstr "指定是否要啟用背景維護模式"
 
@@ -26725,7 +27516,7 @@
 "若要取消並返回 \"git revert\" 前的狀態,\n"
 "請執行 \"git revert --abort\"。"
 
-#: sequencer.c
+#: sequencer.c sequencer.c
 #, c-format
 msgid "could not lock '%s'"
 msgstr "無法鎖定「%s」"
@@ -26735,7 +27526,7 @@
 msgid "could not write eol to '%s'"
 msgstr "無法將換行符號寫入「%s」"
 
-#: sequencer.c
+#: sequencer.c sequencer.c sequencer.c sequencer.c
 #, c-format
 msgid "failed to finalize '%s'"
 msgstr "無法完成 '%s'"
@@ -26905,7 +27696,7 @@
 msgid " (root-commit)"
 msgstr " (根提交)"
 
-#: sequencer.c
+#: sequencer.c sequencer.c
 msgid "could not parse HEAD"
 msgstr "無法解析 HEAD"
 
@@ -26914,16 +27705,16 @@
 msgid "HEAD %s is not a commit!"
 msgstr "HEAD %s 不是一個提交!"
 
-#: sequencer.c
+#: sequencer.c sequencer.c
 msgid "unable to parse commit author"
 msgstr "不能解析提交作者"
 
-#: sequencer.c
+#: sequencer.c sequencer.c
 #, c-format
 msgid "unable to read commit message from '%s'"
 msgstr "不能從 '%s' 讀取提交說明"
 
-#: sequencer.c
+#: sequencer.c sequencer.c
 #, c-format
 msgid "invalid author identity '%s'"
 msgstr "無效的作者身分 '%s'"
@@ -26932,7 +27723,7 @@
 msgid "corrupt author: missing date information"
 msgstr "作者資訊損壞:缺少日期資訊"
 
-#: sequencer.c
+#: sequencer.c sequencer.c
 #, c-format
 msgid "could not update %s"
 msgstr "無法更新 %s"
@@ -26970,7 +27761,7 @@
 msgid "This is a combination of %d commits."
 msgstr "這是整合 %d 個提交的集合提交。"
 
-#: sequencer.c
+#: sequencer.c sequencer.c
 #, c-format
 msgid "cannot write '%s'"
 msgstr "不能寫 '%s'"
@@ -26979,7 +27770,7 @@
 msgid "need a HEAD to fixup"
 msgstr "需要一個 HEAD 來修復"
 
-#: sequencer.c
+#: sequencer.c sequencer.c
 msgid "could not read HEAD"
 msgstr "無法讀取 HEAD"
 
@@ -27005,7 +27796,7 @@
 msgid "commit %s is a merge but no -m option was given."
 msgstr "提交 %s 是一個合併提交但未提供 -m 選項。"
 
-#: sequencer.c
+#: sequencer.c sequencer.c
 #, c-format
 msgid "commit %s does not have parent %d"
 msgstr "提交 %s 沒有第 %d 個父提交"
@@ -27054,15 +27845,10 @@
 
 #: sequencer.c
 #, c-format
-msgid "'%s' is not a valid refname"
-msgstr "「%s」不是有效的引用名稱"
-
-#: sequencer.c
-#, c-format
 msgid "update-ref requires a fully qualified refname e.g. refs/heads/%s"
 msgstr "update-ref 需要完全限定的引用名稱,比如:refs/heads/%s"
 
-#: sequencer.c
+#: sequencer.c sequencer.c sequencer.c
 #, c-format
 msgid "'%s' does not accept merge commits"
 msgstr "「%s」不接受合併提交"
@@ -27087,9 +27873,9 @@
 "replay the merge and reword the commit message, use\n"
 "'merge -c' on the commit"
 msgstr ""
-"「reword」並無法使用合併提交作為參數。\n"
-"如果你希望合併並改寫提交訊息,\n"
-"請對這個提交使用「merge -c」"
+"「reword」不接受合併提交。如果你希望重放\n"
+"合併動作,並改寫提交訊息,請對這個提交使用\n"
+"「merge -c」"
 
 #. TRANSLATORS: 'edit', 'merge -C' and 'break' should
 #. not be translated.
@@ -27165,6 +27951,18 @@
 msgstr "不能在揀選中執行還原提交。"
 
 #: sequencer.c
+msgid "trailers file contains empty line"
+msgstr "結尾資訊檔案包含空白列"
+
+#: sequencer.c
+msgid "trailers file is empty"
+msgstr "結尾資訊檔案為空"
+
+#: sequencer.c
+msgid "cannot read trailers files"
+msgstr "無法讀取結尾資訊檔案"
+
+#: sequencer.c
 msgid "unusable squash-onto"
 msgstr "不可用的 squash-onto"
 
@@ -27173,7 +27971,7 @@
 msgid "malformed options sheet: '%s'"
 msgstr "格式錯誤的選項清單:'%s'"
 
-#: sequencer.c
+#: sequencer.c sequencer.c
 msgid "empty commit set passed"
 msgstr "提供了空的提交集"
 
@@ -27200,15 +27998,15 @@
 msgid "could not create sequencer directory '%s'"
 msgstr "無法建立序列目錄「%s」"
 
-#: sequencer.c
+#: sequencer.c sequencer.c
 msgid "no cherry-pick or revert in progress"
 msgstr "揀選或還原動作並未進行"
 
-#: sequencer.c
+#: sequencer.c sequencer.c
 msgid "cannot resolve HEAD"
 msgstr "不能解析 HEAD"
 
-#: sequencer.c
+#: sequencer.c sequencer.c
 msgid "cannot abort from a branch yet to be born"
 msgstr "不能從尚未建立的分支終止"
 
@@ -27255,7 +28053,7 @@
 "您已經提交了嗎?\n"
 "試試 \"git %s --continue\""
 
-#: sequencer.c
+#: sequencer.c sequencer.c
 msgid "cannot read HEAD"
 msgstr "不能讀取 HEAD"
 
@@ -27553,7 +28351,7 @@
 "\n"
 "您的工作區中有未提交的變更。請先提交然後再次執行 'git rebase --continue'。"
 
-#: sequencer.c
+#: sequencer.c sequencer.c
 #, c-format
 msgid "could not write file: '%s'"
 msgstr "無法寫入檔案:「%s」"
@@ -27580,12 +28378,12 @@
 msgid "can't revert as initial commit"
 msgstr "不能作為初始提交還原提交"
 
-#: sequencer.c
+#: sequencer.c sequencer.c
 #, c-format
 msgid "skipped previously applied commit %s"
 msgstr "已略過先前套用的 %s 提交"
 
-#: sequencer.c
+#: sequencer.c sequencer.c
 msgid "use --reapply-cherry-picks to include skipped commits"
 msgstr "使用 --reapply-cherry-picks 以包含略過提交"
 
@@ -27597,7 +28395,7 @@
 msgid "make_script: error preparing revisions"
 msgstr "make_script:準備版本時錯誤"
 
-#: sequencer.c
+#: sequencer.c sequencer.c
 msgid "nothing to do"
 msgstr "無事可做"
 
@@ -27609,7 +28407,7 @@
 msgid "the script was already rearranged."
 msgstr "腳本已經重新編排。"
 
-#: sequencer.c
+#: sequencer.c sequencer.c
 #, c-format
 msgid "update-refs file at '%s' is invalid"
 msgstr "位於「%s」的 update-refs 檔案無效"
@@ -27719,7 +28517,7 @@
 msgid "not a git repository: '%s'"
 msgstr "不是一個 git 版本庫:'%s'"
 
-#: setup.c
+#: setup.c setup.c setup.c
 #, c-format
 msgid "cannot chdir to '%s'"
 msgstr "不能切換目錄到 '%s'"
@@ -27734,7 +28532,7 @@
 msgid "safe.directory '%s' not absolute"
 msgstr "safe.directory「%s」不是絕對路徑"
 
-#: setup.c
+#: setup.c setup.c
 #, c-format
 msgid ""
 "detected dubious ownership in repository at '%s'\n"
@@ -27748,10 +28546,20 @@
 "\tgit config --global --add safe.directory %s"
 
 #: setup.c
+#, c-format
+msgid "error reading '%s'"
+msgstr "讀取「%s」時發生錯誤"
+
+#: setup.c
+#, c-format
+msgid "not a regular file: '%s'"
+msgstr "不是一般檔案:「%s」"
+
+#: setup.c
 msgid "Unable to read current working directory"
 msgstr "不能讀取目前工作目錄"
 
-#: setup.c
+#: setup.c setup.c
 #, c-format
 msgid "cannot change to '%s'"
 msgstr "不能切換到 '%s'"
@@ -27775,6 +28583,11 @@
 msgid "cannot use bare repository '%s' (safe.bareRepository is '%s')"
 msgstr "無法使用「%s」純版本庫(safe.bareRepository 是「%s」)"
 
+#: setup.c setup.c
+#, c-format
+msgid "unknown ref storage format: '%s'"
+msgstr "未知的引用儲存格式:「%s」"
+
 #: setup.c
 #, c-format
 msgid ""
@@ -27861,7 +28674,7 @@
 "attempt to reinitialize repository with different reference storage format"
 msgstr "嘗試以不同的引用儲存格式重新初始化版本庫"
 
-#: setup.c
+#: setup.c setup.c
 #, c-format
 msgid "%s already exists"
 msgstr "%s 已經存在"
@@ -27913,6 +28726,7 @@
 msgid "bad %s format: %%%.*s"
 msgstr "無效的 %s 格式:%%%.*s"
 
+#: strbuf.c strbuf.c strbuf.c strbuf.c strbuf.c
 #: strbuf.c
 #, c-format
 msgid "%u.%2.2u"
@@ -28008,11 +28822,11 @@
 msgid "Could not update .gitmodules entry %s"
 msgstr "無法更新 .gitmodules 條目 %s"
 
-#: submodule.c
+#: submodule.c submodule.c
 msgid "Cannot change unmerged .gitmodules, resolve merge conflicts first"
 msgstr "無法修改未合併的 .gitmodules,先解決合併衝突"
 
-#: submodule.c
+#: submodule.c submodule.c
 #, c-format
 msgid "Could not find section in .gitmodules where path=%s"
 msgstr "無法在 .gitmodules 中找到 path=%s 的小節"
@@ -28134,7 +28948,7 @@
 msgid "Could not unset core.worktree setting in submodule '%s'"
 msgstr "無法在子模組「%s」中取消 core.worktree 的設定"
 
-#: submodule.c
+#: submodule.c submodule.c
 #, c-format
 msgid "could not recurse into submodule '%s'"
 msgstr "無法遞迴子模組路徑 '%s'"
@@ -28174,18 +28988,13 @@
 "relocate_gitdir for submodule '%s' with more than one worktree not supported"
 msgstr "不支援對有多個工作區的子模組 '%s' 執行 relocate_gitdir"
 
-#: submodule.c
+#: submodule.c submodule.c
 #, c-format
 msgid "could not lookup name for submodule '%s'"
 msgstr "無法查詢子模組「%s」的名稱"
 
 #: submodule.c
 #, c-format
-msgid "refusing to move '%s' into an existing git dir"
-msgstr "拒絕移動「%s」至現存 git 目錄"
-
-#: submodule.c
-#, c-format
 msgid ""
 "Migrating git directory of '%s%s' from\n"
 "'%s' to\n"
@@ -28204,6 +29013,30 @@
 msgid "ls-tree returned unexpected return code %d"
 msgstr "ls-tree 返回未知返回值 %d"
 
+#: submodule.c
+#, c-format
+msgid ""
+"the 'submodule.%s.gitdir' config does not exist for module '%s'. Please "
+"ensure it is set, for example by running something like: 'git config "
+"submodule.%s.gitdir .git/modules/%s'. For details see the "
+"extensions.submodulePathConfig documentation."
+msgstr ""
+"模組「%2$s」沒有「submodule.%1$s.gitdir」組態。請確定是否設定完成,舉例來說,"
+"可以執行類似這樣的命令進行設定:「git config submodule.%3$s.gitdir .git/"
+"modules/%4$s」。詳細資訊請參閱 extensions.submodulePathConfig 的說明文件。"
+
+#: submodule.c
+msgid ""
+"enabling extensions.submodulePathConfig might fix the following error, if "
+"it's not already enabled."
+msgstr ""
+"如果尚未啟用 extensions.submodulePathConfig,啟用這個選項可能會修復以下錯誤。"
+
+#: submodule.c
+#, c-format
+msgid "refusing to create/use '%s' in another submodule's  git dir."
+msgstr "拒絕在另一個子模組的 git 目錄中建立或使用「%s」。"
+
 #: symlinks.c
 #, c-format
 msgid "failed to lstat '%s'"
@@ -28275,6 +29108,11 @@
 msgstr "太多提交標記為可以取得"
 
 #: t/helper/test-read-midx.c
+#, c-format
+msgid "could not find MIDX with checksum %s"
+msgstr "找不到總和檢查碼為 %s 的 MIDX"
+
+#: t/helper/test-read-midx.c
 msgid "could not determine MIDX preferred pack"
 msgstr "無法確定 MIDX 偏好的封裝"
 
@@ -28383,6 +29221,7 @@
 msgid "running trailer command '%s' failed"
 msgstr "執行 trailer 指令 '%s' 失敗"
 
+#: trailer.c trailer.c trailer.c trailer.c trailer.c
 #: trailer.c
 #, c-format
 msgid "unknown value '%s' for key '%s'"
@@ -28393,7 +29232,35 @@
 msgid "empty trailer token in trailer '%.*s'"
 msgstr "簽名 '%.*s' 的鍵為空"
 
-#: transport-helper.c
+#: trailer.c trailer.c
+msgid "empty --trailer argument"
+msgstr "--trailer 引數為空"
+
+#: trailer.c
+#, c-format
+msgid "invalid trailer '%s': missing key before separator"
+msgstr "無效的結尾資訊「%s」:分隔符號前缺少鍵"
+
+#: trailer.c wrapper.c wrapper.c
+#, c-format
+msgid "could not stat %s"
+msgstr "無法 stat %s"
+
+#: trailer.c
+#, c-format
+msgid "file %s is not a regular file"
+msgstr "檔案 %s 不是一個正規檔案"
+
+#: trailer.c
+#, c-format
+msgid "file %s is not writable by user"
+msgstr "檔案 %s 使用者不可寫"
+
+#: trailer.c
+msgid "could not write to temporary file"
+msgstr "無法寫入暫存檔案"
+
+#: transport-helper.c transport-helper.c
 msgid "full write to remote helper failed"
 msgstr "完整寫入遠端協助工具失敗"
 
@@ -28402,7 +29269,7 @@
 msgid "unable to find remote helper for '%s'"
 msgstr "無法為 '%s' 找到遠端協助工具"
 
-#: transport-helper.c
+#: transport-helper.c transport-helper.c
 msgid "can't dup helper output fd"
 msgstr "無法再製協助工具輸出檔案句柄"
 
@@ -28417,7 +29284,7 @@
 msgid "this remote helper should implement refspec capability"
 msgstr "遠端協助工具需要實現 refspec 引用規格能力"
 
-#: transport-helper.c
+#: transport-helper.c transport-helper.c
 #, c-format
 msgid "%s unexpectedly said: '%s'"
 msgstr "%s 意外地說:'%s'"
@@ -28435,7 +29302,7 @@
 msgid "error while running fast-import"
 msgstr "執行 fast-import 發生錯誤"
 
-#: transport-helper.c
+#: transport-helper.c transport-helper.c
 #, c-format
 msgid "could not read ref %s"
 msgstr "無法讀取引用 %s"
@@ -28562,7 +29429,7 @@
 msgid "%s thread failed to join: %s"
 msgstr "%s 執行緒等待失敗:%s"
 
-#: transport-helper.c
+#: transport-helper.c transport-helper.c
 #, c-format
 msgid "can't start thread for copying data: %s"
 msgstr "無法啟動執行緒來複製資料: %s"
@@ -28577,7 +29444,7 @@
 msgid "%s process failed"
 msgstr "%s 進程失敗"
 
-#: transport-helper.c
+#: transport-helper.c transport-helper.c
 msgid "can't start thread for copying data"
 msgstr "無法啟動執行緒來複製資料"
 
@@ -28962,7 +29829,7 @@
 msgid "invalid URL scheme name or missing '://' suffix"
 msgstr "無效的 URL 方案名稱或遺失 '://' 後綴"
 
-#: urlmatch.c
+#: urlmatch.c urlmatch.c urlmatch.c
 #, c-format
 msgid "invalid %XX escape sequence"
 msgstr "無效的 %XX 轉義序列"
@@ -28979,7 +29846,7 @@
 msgid "invalid characters in host name"
 msgstr "主機名稱中包含無效的字元"
 
-#: urlmatch.c
+#: urlmatch.c urlmatch.c
 msgid "invalid port number"
 msgstr "無效的埠號"
 
@@ -28992,7 +29859,7 @@
 msgid "error: unable to format message: %s\n"
 msgstr "錯誤:無法格式化訊息:%s\n"
 
-#: usage.c
+#: usage.c usage.c
 msgid "usage: "
 msgstr "用法: "
 
@@ -29126,7 +29993,7 @@
 msgid "gitdir file does not exist"
 msgstr "找不到 gitdir 檔案"
 
-#: worktree.c
+#: worktree.c worktree.c
 #, c-format
 msgid "unable to read gitdir file (%s)"
 msgstr "無法讀取 gitdir 檔案 (%s)"
@@ -29176,12 +30043,12 @@
 msgid "unable to create '%s'"
 msgstr "不能建立 '%s'"
 
-#: wrapper.c
+#: wrapper.c wrapper.c
 #, c-format
 msgid "could not open '%s' for reading and writing"
 msgstr "無法開啟「%s」進行讀寫"
 
-#: wrapper.c
+#: wrapper.c wrapper.c
 #, c-format
 msgid "unable to access '%s'"
 msgstr "不能存取 '%s'"
@@ -29209,18 +30076,18 @@
 msgstr "未合併的路徑:"
 
 #  譯者:請維持前導空格
-#: wt-status.c
+#: wt-status.c wt-status.c
 msgid "  (use \"git restore --staged <file>...\" to unstage)"
 msgstr "  (使用 \"git restore --staged <檔案>...\" 以取消暫存)"
 
 #  譯者:請維持前導空格
-#: wt-status.c
+#: wt-status.c wt-status.c
 #, c-format
 msgid "  (use \"git restore --source=%s --staged <file>...\" to unstage)"
 msgstr "  (使用 \"git restore --source=%s --staged <檔案>...\" 以取消暫存)"
 
 #  譯者:請維持前導空格
-#: wt-status.c
+#: wt-status.c wt-status.c
 msgid "  (use \"git rm --cached <file>...\" to unstage)"
 msgstr "  (使用 \"git rm --cached <檔案>...\" 以取消暫存)"
 
@@ -29230,7 +30097,7 @@
 msgstr "  (使用 \"git add <檔案>...\" 標記解決方案)"
 
 #  譯者:請維持前導空格
-#: wt-status.c
+#: wt-status.c wt-status.c
 msgid "  (use \"git add/rm <file>...\" as appropriate to mark resolution)"
 msgstr "  (酌情使用 \"git add/rm <檔案>...\" 標記解決方案)"
 
@@ -29239,11 +30106,11 @@
 msgid "  (use \"git rm <file>...\" to mark resolution)"
 msgstr "  (使用 \"git rm <檔案>...\" 標記解決方案)"
 
-#: wt-status.c
+#: wt-status.c wt-status.c
 msgid "Changes to be committed:"
 msgstr "要提交的變更:"
 
-#: wt-status.c
+#: wt-status.c wt-status.c
 msgid "Changes not staged for commit:"
 msgstr "尚未暫存以備提交的變更:"
 
@@ -29723,7 +30590,7 @@
 msgid "nothing to commit (create/copy files and use \"git add\" to track)\n"
 msgstr "沒有東西提交(建立/複製檔案並使用「git add」追蹤之)\n"
 
-#: wt-status.c
+#: wt-status.c wt-status.c
 #, c-format
 msgid "nothing to commit\n"
 msgstr "無檔案要提交\n"
@@ -29753,11 +30620,11 @@
 msgstr "不同"
 
 #  譯者:請維持句尾空格
-#: wt-status.c
+#: wt-status.c wt-status.c
 msgid "behind "
 msgstr "落後 "
 
-#: wt-status.c
+#: wt-status.c wt-status.c
 msgid "ahead "
 msgstr "領先 "
 
@@ -29819,7 +30686,7 @@
 msgid "Simple merge did not work, trying automatic merge."
 msgstr "簡單合併未生效,嘗試自動合併。"
 
-#: git-sh-setup.sh
+#: git-sh-setup.sh git-sh-setup.sh
 #, sh-format
 msgid "usage: $dashless $USAGE"
 msgstr "用法:$dashless $USAGE"
@@ -29829,7 +30696,7 @@
 msgid "Cannot chdir to $cdup, the toplevel of the working tree"
 msgstr "不能切換目錄到 $cdup,工作區的頂級目錄"
 
-#: git-sh-setup.sh
+#: git-sh-setup.sh git-sh-setup.sh
 #, sh-format
 msgid "fatal: $program_name cannot be used without a working tree."
 msgstr "致命錯誤:$program_name 不能在沒有工作區的情況下使用。"
@@ -29864,7 +30731,7 @@
 msgid "local zone differs from GMT by a non-minute interval\n"
 msgstr "本機時間和 GMT 有不到一分鐘間隔\n"
 
-#: git-send-email.perl
+#: git-send-email.perl git-send-email.perl
 msgid "local time offset greater than or equal to 24 hours\n"
 msgstr "本機時間位移量大於等於 24 小時\n"
 
@@ -29906,7 +30773,7 @@
 "git-send-email 已經以 sendemail.* 選項設定 - 注意裡面的 'e'。\n"
 "請將 sendemail.forbidSendmailVariables 設為 false 停用此檢查。\n"
 
-#: git-send-email.perl
+#: git-send-email.perl git-send-email.perl
 msgid "Cannot run git format-patch from outside a repository\n"
 msgstr "不能在版本庫之外執行 git format-patch\n"
 
@@ -30026,8 +30893,13 @@
 msgstr "如下檔案含 8bit 內容,但沒有聲明一個 Content-Transfer-Encoding。\n"
 
 #: git-send-email.perl
-msgid "Which 8bit encoding should I declare [UTF-8]? "
-msgstr "要宣告 8bit 為什麼樣的編碼格式 [UTF-8]? "
+msgid "Declare which 8bit encoding to use [default: UTF-8]? "
+msgstr "宣告要使用的 8 位元編碼 [預設值: UTF-8]? "
+
+#: git-send-email.perl
+#, perl-format
+msgid "'%s' does not appear to be a valid charset name. Use it anyway [y/N]? "
+msgstr "「%s」似乎不是有效的字元集名稱。仍要使用它嗎 [y/N]? "
 
 #: git-send-email.perl
 #, perl-format
@@ -30054,7 +30926,7 @@
 msgid "Message-ID to be used as In-Reply-To for the first email (if any)? "
 msgstr "(如果有)Message-ID 是否要被用作第一封信件的 In-Reply-To ? "
 
-#: git-send-email.perl
+#: git-send-email.perl git-send-email.perl
 #, perl-format
 msgid "error: unable to extract a valid address from: %s\n"
 msgstr "錯誤:不能從 %s 中擷取一個有效的信件位址\n"
@@ -30072,6 +30944,11 @@
 msgstr "CA 路徑 \"%s\" 不存在"
 
 #: git-send-email.perl
+#, perl-format
+msgid "Only client key \"%s\" specified"
+msgstr "只有指定用戶端金鑰「%s」"
+
+#: git-send-email.perl
 msgid ""
 "    The Cc list above has been expanded by additional\n"
 "    addresses found in the patch commit message. By default\n"
@@ -30117,7 +30994,7 @@
 msgid "Server does not support STARTTLS! %s"
 msgstr "伺服器不支援 STARTTLS!%s"
 
-#: git-send-email.perl
+#: git-send-email.perl git-send-email.perl
 #, perl-format
 msgid "STARTTLS failed! %s"
 msgstr "STARTTLS 失敗!%s"
@@ -30171,7 +31048,7 @@
 msgid "can't open file %s"
 msgstr "無法開啟檔案 %s"
 
-#: git-send-email.perl
+#: git-send-email.perl git-send-email.perl
 #, perl-format
 msgid "(mbox) Adding cc: %s from line '%s'\n"
 msgstr "(mbox) 新增 cc:%s 自行 '%s'\n"
@@ -30235,7 +31112,7 @@
 "%s\n"
 "警告:修補檔未能傳送\n"
 
-#: git-send-email.perl
+#: git-send-email.perl git-send-email.perl git-send-email.perl
 #, perl-format
 msgid "unable to open %s: %s\n"
 msgstr "無法開啟 %s: %s\n"
diff --git a/pretty.c b/pretty.c
index 8148039..7328aec 100644
--- a/pretty.c
+++ b/pretty.c
@@ -399,7 +399,6 @@ static void add_rfc2047(struct strbuf *sb, const char *line, size_t len,
 	int i;
 	int line_len = last_line_length(sb);
 
-	strbuf_grow(sb, len * 3 + strlen(encoding) + 100);
 	strbuf_addf(sb, "=?%s?q?", encoding);
 	line_len += strlen(encoding) + 5; /* 5 for =??q? */
 
diff --git a/promisor-remote.c b/promisor-remote.c
index 225260b..f34856d 100644
--- a/promisor-remote.c
+++ b/promisor-remote.c
@@ -268,11 +268,35 @@ static int remove_fetched_oids(struct repository *repo,
 	return remaining_nr;
 }
 
+static int try_promisor_remotes(struct repository *repo,
+				struct object_id **remaining_oids,
+				int *remaining_nr, int *to_free,
+				bool accepted_only)
+{
+	struct promisor_remote *r = repo->promisor_remote_config->promisors;
+
+	for (; r; r = r->next) {
+		if (accepted_only != r->accepted)
+			continue;
+		if (fetch_objects(repo, r->name, *remaining_oids, *remaining_nr) < 0) {
+			if (*remaining_nr == 1)
+				continue;
+			*remaining_nr = remove_fetched_oids(repo, remaining_oids,
+							    *remaining_nr, *to_free);
+			if (*remaining_nr) {
+				*to_free = 1;
+				continue;
+			}
+		}
+		return 1; /* all fetched */
+	}
+	return 0;
+}
+
 void promisor_remote_get_direct(struct repository *repo,
 				const struct object_id *oids,
 				int oid_nr)
 {
-	struct promisor_remote *r;
 	struct object_id *remaining_oids = (struct object_id *)oids;
 	int remaining_nr = oid_nr;
 	int to_free = 0;
@@ -283,19 +307,13 @@ void promisor_remote_get_direct(struct repository *repo,
 
 	promisor_remote_init(repo);
 
-	for (r = repo->promisor_remote_config->promisors; r; r = r->next) {
-		if (fetch_objects(repo, r->name, remaining_oids, remaining_nr) < 0) {
-			if (remaining_nr == 1)
-				continue;
-			remaining_nr = remove_fetched_oids(repo, &remaining_oids,
-							 remaining_nr, to_free);
-			if (remaining_nr) {
-				to_free = 1;
-				continue;
-			}
-		}
+	/* Try accepted remotes first (those the server told us to use) */
+	if (try_promisor_remotes(repo, &remaining_oids, &remaining_nr,
+				 &to_free, true))
 		goto all_fetched;
-	}
+	if (try_promisor_remotes(repo, &remaining_oids, &remaining_nr,
+				 &to_free, false))
+		goto all_fetched;
 
 	for (i = 0; i < remaining_nr; i++) {
 		if (is_promisor_object(repo, &remaining_oids[i]))
@@ -557,6 +575,12 @@ enum accept_promisor {
 	ACCEPT_ALL
 };
 
+/*
+ * Check if a specific field and its advertised value match the local
+ * configuration of a given promisor remote.
+ *
+ * Returns 1 if they match, 0 otherwise.
+ */
 static int match_field_against_config(const char *field, const char *value,
 				      struct promisor_info *config_info)
 {
@@ -568,9 +592,18 @@ static int match_field_against_config(const char *field, const char *value,
 	return 0;
 }
 
+/*
+ * Check that the advertised fields match the local configuration.
+ *
+ * When 'config_entry' is NULL (ACCEPT_ALL mode), every checked field
+ * must match at least one remote in 'config_info'.
+ *
+ * When 'config_entry' points to a specific remote's config, the
+ * checked fields are compared against that single remote only.
+ */
 static int all_fields_match(struct promisor_info *advertised,
 			    struct string_list *config_info,
-			    int in_list)
+			    struct promisor_info *config_entry)
 {
 	struct string_list *fields = fields_checked();
 	struct string_list_item *item_checked;
@@ -579,7 +612,6 @@ static int all_fields_match(struct promisor_info *advertised,
 		int match = 0;
 		const char *field = item_checked->string;
 		const char *value = NULL;
-		struct string_list_item *item;
 
 		if (!strcasecmp(field, promisor_field_filter))
 			value = advertised->filter;
@@ -589,7 +621,11 @@ static int all_fields_match(struct promisor_info *advertised,
 		if (!value)
 			return 0;
 
-		if (in_list) {
+		if (config_entry) {
+			match = match_field_against_config(field, value,
+							   config_entry);
+		} else {
+			struct string_list_item *item;
 			for_each_string_list_item(item, config_info) {
 				struct promisor_info *p = item->util;
 				if (match_field_against_config(field, value, p)) {
@@ -597,12 +633,6 @@ static int all_fields_match(struct promisor_info *advertised,
 					break;
 				}
 			}
-		} else {
-			item = string_list_lookup(config_info, advertised->name);
-			if (item) {
-				struct promisor_info *p = item->util;
-				match = match_field_against_config(field, value, p);
-			}
 		}
 
 		if (!match)
@@ -612,6 +642,14 @@ static int all_fields_match(struct promisor_info *advertised,
 	return 1;
 }
 
+static bool has_control_char(const char *s)
+{
+	for (const char *c = s; *c; c++)
+		if (iscntrl(*c))
+			return true;
+	return false;
+}
+
 static int should_accept_remote(enum accept_promisor accept,
 				struct promisor_info *advertised,
 				struct string_list *config_info)
@@ -621,8 +659,13 @@ static int should_accept_remote(enum accept_promisor accept,
 	const char *remote_name = advertised->name;
 	const char *remote_url = advertised->url;
 
+	if (!remote_url || !*remote_url)
+		BUG("no or empty URL advertised for remote '%s'; "
+		    "this remote should have been rejected earlier",
+		    remote_name);
+
 	if (accept == ACCEPT_ALL)
-		return all_fields_match(advertised, config_info, 1);
+		return all_fields_match(advertised, config_info, NULL);
 
 	/* Get config info for that promisor remote */
 	item = string_list_lookup(config_info, remote_name);
@@ -634,23 +677,19 @@ static int should_accept_remote(enum accept_promisor accept,
 	p = item->util;
 
 	if (accept == ACCEPT_KNOWN_NAME)
-		return all_fields_match(advertised, config_info, 0);
+		return all_fields_match(advertised, config_info, p);
 
 	if (accept != ACCEPT_KNOWN_URL)
 		BUG("Unhandled 'enum accept_promisor' value '%d'", accept);
 
-	if (!remote_url || !*remote_url) {
-		warning(_("no or empty URL advertised for remote '%s'"), remote_name);
+	if (strcmp(p->url, remote_url)) {
+		warning(_("known remote named '%s' but with URL '%s' instead of '%s', "
+			  "ignoring this remote"),
+			remote_name, p->url, remote_url);
 		return 0;
 	}
 
-	if (!strcmp(p->url, remote_url))
-		return all_fields_match(advertised, config_info, 0);
-
-	warning(_("known remote named '%s' but with URL '%s' instead of '%s'"),
-		remote_name, p->url, remote_url);
-
-	return 0;
+	return all_fields_match(advertised, config_info, p);
 }
 
 static int skip_field_name_prefix(const char *elem, const char *field_name, const char **value)
@@ -691,9 +730,9 @@ static struct promisor_info *parse_one_advertised_remote(const char *remote_info
 
 	string_list_clear(&elem_list, 0);
 
-	if (!info->name || !info->url) {
-		warning(_("server advertised a promisor remote without a name or URL: %s"),
-			remote_info);
+	if (!info->name || !*info->name || !info->url || !*info->url) {
+		warning(_("server advertised a promisor remote without a name or URL: '%s', "
+			  "ignoring this remote"), remote_info);
 		promisor_info_free(info);
 		return NULL;
 	}
@@ -741,18 +780,14 @@ static bool valid_filter(const char *filter, const char *remote_name)
 	return !res;
 }
 
-/* Check that a token doesn't contain any control character */
 static bool valid_token(const char *token, const char *remote_name)
 {
-	const char *c = token;
-
-	for (; *c; c++)
-		if (iscntrl(*c)) {
-			warning(_("invalid token '%s' for remote '%s' "
-				  "will not be stored"),
-				token, remote_name);
-			return false;
-		}
+	if (has_control_char(token)) {
+		warning(_("invalid token '%s' for remote '%s' "
+			  "will not be stored"),
+			token, remote_name);
+		return false;
+	}
 
 	return true;
 }
@@ -827,20 +862,12 @@ static bool promisor_store_advertised_fields(struct promisor_info *advertised,
 	return reload_config;
 }
 
-static void filter_promisor_remote(struct repository *repo,
-				   struct strvec *accepted,
-				   const char *info)
+static enum accept_promisor accept_from_server(struct repository *repo)
 {
 	const char *accept_str;
 	enum accept_promisor accept = ACCEPT_NONE;
-	struct string_list config_info = STRING_LIST_INIT_NODUP;
-	struct string_list remote_info = STRING_LIST_INIT_DUP;
-	struct store_info *store_info = NULL;
-	struct string_list_item *item;
-	bool reload_config = false;
-	struct string_list accepted_filters = STRING_LIST_INIT_DUP;
 
-	if (!repo_config_get_string_tmp(the_repository, "promisor.acceptfromserver", &accept_str)) {
+	if (!repo_config_get_string_tmp(repo, "promisor.acceptfromserver", &accept_str)) {
 		if (!*accept_str || !strcasecmp("None", accept_str))
 			accept = ACCEPT_NONE;
 		else if (!strcasecmp("KnownUrl", accept_str))
@@ -854,6 +881,20 @@ static void filter_promisor_remote(struct repository *repo,
 				accept_str, "promisor.acceptfromserver");
 	}
 
+	return accept;
+}
+
+static void filter_promisor_remote(struct repository *repo,
+				   struct string_list *accepted_remotes,
+				   const char *info)
+{
+	struct string_list config_info = STRING_LIST_INIT_NODUP;
+	struct string_list remote_info = STRING_LIST_INIT_DUP;
+	struct store_info *store_info = NULL;
+	struct string_list_item *item;
+	bool reload_config = false;
+	enum accept_promisor accept = accept_from_server(repo);
+
 	if (accept == ACCEPT_NONE)
 		return;
 
@@ -880,17 +921,10 @@ static void filter_promisor_remote(struct repository *repo,
 			if (promisor_store_advertised_fields(advertised, store_info))
 				reload_config = true;
 
-			strvec_push(accepted, advertised->name);
-
-			/* Capture advertised filters for accepted remotes */
-			if (advertised->filter) {
-				struct string_list_item *i;
-				i = string_list_append(&accepted_filters, advertised->name);
-				i->util = xstrdup(advertised->filter);
-			}
+			string_list_append(accepted_remotes, advertised->name)->util = advertised;
+		} else {
+			promisor_info_free(advertised);
 		}
-
-		promisor_info_free(advertised);
 	}
 
 	promisor_info_list_clear(&config_info);
@@ -900,39 +934,36 @@ static void filter_promisor_remote(struct repository *repo,
 	if (reload_config)
 		repo_promisor_remote_reinit(repo);
 
-	/* Apply accepted remote filters to the stable repo state */
-	for_each_string_list_item(item, &accepted_filters) {
-		struct promisor_remote *r = repo_promisor_remote_find(repo, item->string);
+	/* Apply accepted remotes to the stable repo state */
+	for_each_string_list_item(item, accepted_remotes) {
+		struct promisor_info *info = item->util;
+		struct promisor_remote *r = repo_promisor_remote_find(repo, info->name);
+
 		if (r) {
-			free(r->advertised_filter);
-			r->advertised_filter = item->util;
-			item->util = NULL;
-		}
-	}
-
-	string_list_clear(&accepted_filters, 1);
-
-	/* Mark the remotes as accepted in the repository state */
-	for (size_t i = 0; i < accepted->nr; i++) {
-		struct promisor_remote *r = repo_promisor_remote_find(repo, accepted->v[i]);
-		if (r)
 			r->accepted = 1;
+			if (info->filter) {
+				free(r->advertised_filter);
+				r->advertised_filter = xstrdup(info->filter);
+			}
+		}
 	}
 }
 
 void promisor_remote_reply(const char *info, char **accepted_out)
 {
-	struct strvec accepted = STRVEC_INIT;
+	struct string_list accepted_remotes = STRING_LIST_INIT_NODUP;
 
-	filter_promisor_remote(the_repository, &accepted, info);
+	filter_promisor_remote(the_repository, &accepted_remotes, info);
 
 	if (accepted_out) {
-		if (accepted.nr) {
+		if (accepted_remotes.nr) {
 			struct strbuf reply = STRBUF_INIT;
-			for (size_t i = 0; i < accepted.nr; i++) {
-				if (i)
+			struct string_list_item *item;
+
+			for_each_string_list_item(item, &accepted_remotes) {
+				if (reply.len)
 					strbuf_addch(&reply, ';');
-				strbuf_addstr_urlencode(&reply, accepted.v[i], allow_unsanitized);
+				strbuf_addstr_urlencode(&reply, item->string, allow_unsanitized);
 			}
 			*accepted_out = strbuf_detach(&reply, NULL);
 		} else {
@@ -940,7 +971,7 @@ void promisor_remote_reply(const char *info, char **accepted_out)
 		}
 	}
 
-	strvec_clear(&accepted);
+	promisor_info_list_clear(&accepted_remotes);
 }
 
 void mark_promisor_remotes_as_accepted(struct repository *r, const char *remotes)
diff --git a/promisor-remote.h b/promisor-remote.h
index 3d4d2de..301f5ac 100644
--- a/promisor-remote.h
+++ b/promisor-remote.h
@@ -29,6 +29,17 @@ int repo_has_promisor_remote(struct repository *r);
  * Fetches all requested objects from all promisor remotes, trying them one at
  * a time until all objects are fetched.
  *
+ * Callers are responsible for filtering out OIDs that are already present
+ * locally before calling this function: every supplied OID is sent in the
+ * fetch request, even if the object already exists in the local object
+ * store. (Only after a fetch failure does this function fall back to
+ * stripping already-present OIDs from the list before trying the next
+ * configured promisor remote.) Callers should also deduplicate the OIDs.
+ *
+ * To test for local presence without triggering a lazy fetch (which would
+ * defeat the purpose of batching), use odb_has_object(..., 0) or
+ * odb_read_object_info_extended() with OBJECT_INFO_FOR_PREFETCH.
+ *
  * If oid_nr is 0, this function returns immediately.
  */
 void promisor_remote_get_direct(struct repository *repo,
diff --git a/pseudo-merge.c b/pseudo-merge.c
index a2d5bd8..22b8600 100644
--- a/pseudo-merge.c
+++ b/pseudo-merge.c
@@ -150,7 +150,10 @@ static int pseudo_merge_config(const char *var, const char *value,
 	if (!strcmp(key, "pattern")) {
 		struct strbuf re = STRBUF_INIT;
 
-		free(group->pattern);
+		if (group->pattern) {
+			regfree(group->pattern);
+			free(group->pattern);
+		}
 		if (*value != '^')
 			strbuf_addch(&re, '^');
 		strbuf_addstr(&re, value);
@@ -169,8 +172,8 @@ static int pseudo_merge_config(const char *var, const char *value,
 		}
 	} else if (!strcmp(key, "samplerate")) {
 		group->sample_rate = git_config_double(var, value, ctx->kvi);
-		if (!(0 <= group->sample_rate && group->sample_rate <= 1)) {
-			warning(_("%s must be between 0 and 1, using default"), var);
+		if (!(0 < group->sample_rate && group->sample_rate <= 1)) {
+			warning(_("%s must be between 0 (exclusive) and 1, using default"), var);
 			group->sample_rate = DEFAULT_PSEUDO_MERGE_SAMPLE_RATE;
 		}
 	} else if (!strcmp(key, "threshold")) {
@@ -236,6 +239,8 @@ static int find_pseudo_merge_group_for_ref(const struct reference *ref, void *_d
 	c = lookup_commit(the_repository, maybe_peeled);
 	if (!c)
 		return 0;
+	if (repo_parse_commit(the_repository, c))
+		return 0;
 	if (!packlist_find(writer->to_pack, maybe_peeled))
 		return 0;
 
@@ -559,9 +564,9 @@ static struct pseudo_merge *pseudo_merge_at(const struct pseudo_merge_map *pm,
 		if (got == want)
 			return use_pseudo_merge(pm, &pm->v[mi]);
 		else if (got < want)
-			hi = mi;
-		else
 			lo = mi + 1;
+		else
+			hi = mi;
 	}
 
 	warning(_("could not find pseudo-merge for commit %s at offset %"PRIuMAX),
@@ -600,7 +605,7 @@ static int nth_pseudo_merge_ext(const struct pseudo_merge_map *pm,
 		return error(_("out-of-bounds read: (%"PRIuMAX" >= %"PRIuMAX")"),
 			     (uintmax_t)ofs, (uintmax_t)pm->map_size);
 
-	read_pseudo_merge_commit_at(merge, pm->map + ofs);
+	merge->pseudo_merge_ofs = ofs;
 
 	return 0;
 }
@@ -638,14 +643,21 @@ static int pseudo_merge_commit_cmp(const void *va, const void *vb)
 	return 0;
 }
 
-static struct pseudo_merge_commit *find_pseudo_merge(const struct pseudo_merge_map *pm,
-						     uint32_t pos)
+static int find_pseudo_merge(const struct pseudo_merge_map *pm, uint32_t pos,
+			     struct pseudo_merge_commit *out)
 {
-	if (!pm->commits_nr)
-		return NULL;
+	const unsigned char *at;
 
-	return bsearch(&pos, pm->commits, pm->commits_nr,
-		       PSEUDO_MERGE_COMMIT_RAWSZ, pseudo_merge_commit_cmp);
+	if (!pm->commits_nr)
+		return 0;
+
+	at = bsearch(&pos, pm->commits, pm->commits_nr,
+		     PSEUDO_MERGE_COMMIT_RAWSZ, pseudo_merge_commit_cmp);
+	if (!at)
+		return 0;
+
+	read_pseudo_merge_commit_at(out, at);
+	return 1;
 }
 
 int apply_pseudo_merges_for_commit(const struct pseudo_merge_map *pm,
@@ -653,19 +665,18 @@ int apply_pseudo_merges_for_commit(const struct pseudo_merge_map *pm,
 				   struct commit *commit, uint32_t commit_pos)
 {
 	struct pseudo_merge *merge;
-	struct pseudo_merge_commit *merge_commit;
+	struct pseudo_merge_commit merge_commit;
 	int ret = 0;
 
-	merge_commit = find_pseudo_merge(pm, commit_pos);
-	if (!merge_commit)
+	if (!find_pseudo_merge(pm, commit_pos, &merge_commit))
 		return 0;
 
-	if (merge_commit->pseudo_merge_ofs & ((uint64_t)1<<63)) {
+	if (merge_commit.pseudo_merge_ofs & ((uint64_t)1<<63)) {
 		struct pseudo_merge_commit_ext ext = { 0 };
-		off_t ofs = merge_commit->pseudo_merge_ofs & ~((uint64_t)1<<63);
+		off_t ofs = merge_commit.pseudo_merge_ofs & ~((uint64_t)1<<63);
 		uint32_t i;
 
-		if (pseudo_merge_ext_at(pm, &ext, ofs) < -1) {
+		if (pseudo_merge_ext_at(pm, &ext, ofs) < 0) {
 			warning(_("could not read extended pseudo-merge table "
 				  "for commit %s"),
 				oid_to_hex(&commit->object.oid));
@@ -673,11 +684,11 @@ int apply_pseudo_merges_for_commit(const struct pseudo_merge_map *pm,
 		}
 
 		for (i = 0; i < ext.nr; i++) {
-			if (nth_pseudo_merge_ext(pm, &ext, merge_commit, i) < 0)
+			if (nth_pseudo_merge_ext(pm, &ext, &merge_commit, i) < 0)
 				return ret;
 
 			merge = pseudo_merge_at(pm, &commit->object.oid,
-						merge_commit->pseudo_merge_ofs);
+						merge_commit.pseudo_merge_ofs);
 
 			if (!merge)
 				return ret;
@@ -687,7 +698,7 @@ int apply_pseudo_merges_for_commit(const struct pseudo_merge_map *pm,
 		}
 	} else {
 		merge = pseudo_merge_at(pm, &commit->object.oid,
-					merge_commit->pseudo_merge_ofs);
+					merge_commit.pseudo_merge_ofs);
 
 		if (!merge)
 			return ret;
diff --git a/quote.c b/quote.c
index b9f6bdc..235fac8 100644
--- a/quote.c
+++ b/quote.c
@@ -171,9 +171,7 @@ char *sq_dequote(char *arg)
 	return sq_dequote_step(arg, NULL);
 }
 
-static int sq_dequote_to_argv_internal(char *arg,
-				       const char ***argv, int *nr, int *alloc,
-				       struct strvec *array)
+int sq_dequote_to_strvec(char *arg, struct strvec *array)
 {
 	char *next = arg;
 
@@ -191,27 +189,12 @@ static int sq_dequote_to_argv_internal(char *arg,
 				c = *++next;
 			} while (isspace(c));
 		}
-		if (argv) {
-			ALLOC_GROW(*argv, *nr + 1, *alloc);
-			(*argv)[(*nr)++] = dequoted;
-		}
-		if (array)
-			strvec_push(array, dequoted);
+		strvec_push(array, dequoted);
 	} while (next);
 
 	return 0;
 }
 
-int sq_dequote_to_argv(char *arg, const char ***argv, int *nr, int *alloc)
-{
-	return sq_dequote_to_argv_internal(arg, argv, nr, alloc, NULL);
-}
-
-int sq_dequote_to_strvec(char *arg, struct strvec *array)
-{
-	return sq_dequote_to_argv_internal(arg, NULL, NULL, NULL, array);
-}
-
 /* 1 means: quote as octal
  * 0 means: quote as octal if (quote_path_fully)
  * -1 means: never quote
diff --git a/quote.h b/quote.h
index 0300c29..989f238 100644
--- a/quote.h
+++ b/quote.h
@@ -2,6 +2,7 @@
 #define QUOTE_H
 
 struct strbuf;
+struct strvec;
 
 extern int quote_path_fully;
 
@@ -67,17 +68,10 @@ char *sq_dequote_step(char *src, char **next);
 
 /*
  * Same as the above, but can be used to unwrap many arguments in the
- * same string separated by space. Like sq_quote, it works in place,
- * modifying arg and appending pointers into it to argv.
+ * same string separated by space. The strvec will duplicate and take
+ * ownership of the strings, but note that "arg" is still modified in-place
+ * during parsing.
  */
-int sq_dequote_to_argv(char *arg, const char ***argv, int *nr, int *alloc);
-
-/*
- * Same as above, but store the unquoted strings in a strvec. We will
- * still modify arg in place, but unlike sq_dequote_to_argv, the strvec
- * will duplicate and take ownership of the strings.
- */
-struct strvec;
 int sq_dequote_to_strvec(char *arg, struct strvec *);
 
 int unquote_c_style(struct strbuf *, const char *quoted, const char **endp);
diff --git a/range-diff.c b/range-diff.c
index 2712a9a..8e2dd2e 100644
--- a/range-diff.c
+++ b/range-diff.c
@@ -88,7 +88,7 @@ static int read_patches(const char *range, struct string_list *list,
 	line = contents.buf;
 	size = contents.len;
 	for (; size > 0; size -= len, line += len) {
-		const char *p;
+		char *p;
 		char *eol;
 
 		eol = memchr(line, '\n', size);
diff --git a/read-cache.c b/read-cache.c
index 5049f9b..2182910 100644
--- a/read-cache.c
+++ b/read-cache.c
@@ -20,6 +20,7 @@
 #include "dir.h"
 #include "object-file.h"
 #include "odb.h"
+#include "odb/transaction.h"
 #include "oid-array.h"
 #include "tree.h"
 #include "commit.h"
@@ -2309,13 +2310,9 @@ int do_read_index(struct index_state *istate, const char *path, int must_exist)
 	}
 	munmap((void *)mmap, mmap_size);
 
-	/*
-	 * TODO trace2: replace "the_repository" with the actual repo instance
-	 * that is associated with the given "istate".
-	 */
-	trace2_data_intmax("index", the_repository, "read/version",
+	trace2_data_intmax("index", istate->repo, "read/version",
 			   istate->version);
-	trace2_data_intmax("index", the_repository, "read/cache_nr",
+	trace2_data_intmax("index", istate->repo, "read/cache_nr",
 			   istate->cache_nr);
 
 	/*
@@ -2360,16 +2357,12 @@ int read_index_from(struct index_state *istate, const char *path,
 	if (istate->initialized)
 		return istate->cache_nr;
 
-	/*
-	 * TODO trace2: replace "the_repository" with the actual repo instance
-	 * that is associated with the given "istate".
-	 */
-	trace2_region_enter_printf("index", "do_read_index", the_repository,
+	trace2_region_enter_printf("index", "do_read_index", istate->repo,
 				   "%s", path);
 	trace_performance_enter();
 	ret = do_read_index(istate, path, 0);
 	trace_performance_leave("read cache %s", path);
-	trace2_region_leave_printf("index", "do_read_index", the_repository,
+	trace2_region_leave_printf("index", "do_read_index", istate->repo,
 				   "%s", path);
 
 	split_index = istate->split_index;
@@ -3096,13 +3089,9 @@ static int do_write_index(struct index_state *istate, struct tempfile *tempfile,
 	istate->timestamp.nsec = ST_MTIME_NSEC(st);
 	trace_performance_since(start, "write index, changed mask = %x", istate->cache_changed);
 
-	/*
-	 * TODO trace2: replace "the_repository" with the actual repo instance
-	 * that is associated with the given "istate".
-	 */
-	trace2_data_intmax("index", the_repository, "write/version",
+	trace2_data_intmax("index", istate->repo, "write/version",
 			   istate->version);
-	trace2_data_intmax("index", the_repository, "write/cache_nr",
+	trace2_data_intmax("index", istate->repo, "write/cache_nr",
 			   istate->cache_nr);
 
 	ret = 0;
@@ -3144,14 +3133,10 @@ static int do_write_locked_index(struct index_state *istate,
 		return ret;
 	}
 
-	/*
-	 * TODO trace2: replace "the_repository" with the actual repo instance
-	 * that is associated with the given "istate".
-	 */
-	trace2_region_enter_printf("index", "do_write_index", the_repository,
+	trace2_region_enter_printf("index", "do_write_index", istate->repo,
 				   "%s", get_lock_file_path(lock));
 	ret = do_write_index(istate, lock->tempfile, write_extensions, flags);
-	trace2_region_leave_printf("index", "do_write_index", the_repository,
+	trace2_region_leave_printf("index", "do_write_index", istate->repo,
 				   "%s", get_lock_file_path(lock));
 
 	if (was_full)
@@ -4049,6 +4034,7 @@ int add_files_to_cache(struct repository *repo, const char *prefix,
 	rev.diffopt.format_callback = update_callback;
 	rev.diffopt.format_callback_data = &data;
 	rev.diffopt.flags.override_submodule_config = 1;
+	rev.diffopt.detect_rename = 0; /* staging worktree changes does not need renames */
 	rev.max_count = 0; /* do not compare unmerged paths with stage #2 */
 
 	/*
diff --git a/reflog.c b/reflog.c
index 1460ae9..8233707 100644
--- a/reflog.c
+++ b/reflog.c
@@ -168,7 +168,7 @@ static int tree_is_complete(const struct object_id *oid)
 	complete = 1;
 	while (tree_entry(&desc, &entry)) {
 		if (!odb_has_object(the_repository->objects, &entry.oid,
-				HAS_OBJECT_RECHECK_PACKED | HAS_OBJECT_FETCH_PROMISOR) ||
+				ODB_HAS_OBJECT_RECHECK_PACKED | ODB_HAS_OBJECT_FETCH_PROMISOR) ||
 		    (S_ISDIR(entry.mode) && !tree_is_complete(&entry.oid))) {
 			tree->object.flags |= INCOMPLETE;
 			complete = 0;
diff --git a/refs.c b/refs.c
index 5d1d285..0f3355d 100644
--- a/refs.c
+++ b/refs.c
@@ -425,7 +425,7 @@ int ref_resolves_to_object(const char *refname,
 	if (flags & REF_ISBROKEN)
 		return 0;
 	if (!odb_has_object(repo->objects, oid,
-			    HAS_OBJECT_RECHECK_PACKED | HAS_OBJECT_FETCH_PROMISOR)) {
+			    ODB_HAS_OBJECT_RECHECK_PACKED | ODB_HAS_OBJECT_FETCH_PROMISOR)) {
 		error(_("%s does not point to a valid object!"), refname);
 		return 0;
 	}
@@ -989,7 +989,7 @@ enum ref_worktree_type parse_worktree_ref(const char *maybe_worktree_ref,
 	return REF_WORKTREE_SHARED;
 }
 
-long get_files_ref_lock_timeout_ms(void)
+long get_files_ref_lock_timeout_ms(struct repository *repo)
 {
 	static int configured = 0;
 
@@ -997,7 +997,7 @@ long get_files_ref_lock_timeout_ms(void)
 	static int timeout_ms = 100;
 
 	if (!configured) {
-		repo_config_get_int(the_repository, "core.filesreflocktimeout", &timeout_ms);
+		repo_config_get_int(repo, "core.filesreflocktimeout", &timeout_ms);
 		configured = 1;
 	}
 
@@ -1307,6 +1307,7 @@ struct ref_update *ref_transaction_add_update(
 		const char *refname, unsigned int flags,
 		const struct object_id *new_oid,
 		const struct object_id *old_oid,
+		const struct object_id *peeled,
 		const char *new_target, const char *old_target,
 		const char *committer_info,
 		const char *msg)
@@ -1339,6 +1340,8 @@ struct ref_update *ref_transaction_add_update(
 		update->committer_info = xstrdup_or_null(committer_info);
 		update->msg = normalize_reflog_message(msg);
 	}
+	if (flags & REF_HAVE_PEELED)
+		oidcpy(&update->peeled, peeled);
 
 	/*
 	 * This list is generally used by the backends to avoid duplicates.
@@ -1383,25 +1386,27 @@ static int transaction_refname_valid(const char *refname,
 	return 1;
 }
 
-int ref_transaction_update(struct ref_transaction *transaction,
-			   const char *refname,
-			   const struct object_id *new_oid,
-			   const struct object_id *old_oid,
-			   const char *new_target,
-			   const char *old_target,
-			   unsigned int flags, const char *msg,
-			   struct strbuf *err)
+enum ref_transaction_error ref_transaction_update(struct ref_transaction *transaction,
+						  const char *refname,
+						  const struct object_id *new_oid,
+						  const struct object_id *old_oid,
+						  const char *new_target,
+						  const char *old_target,
+						  unsigned int flags, const char *msg,
+						  struct strbuf *err)
 {
+	struct object_id peeled;
+
 	assert(err);
 
 	if ((flags & REF_FORCE_CREATE_REFLOG) &&
 	    (flags & REF_SKIP_CREATE_REFLOG)) {
 		strbuf_addstr(err, _("refusing to force and skip creation of reflog"));
-		return -1;
+		return REF_TRANSACTION_ERROR_GENERIC;
 	}
 
 	if (!transaction_refname_valid(refname, new_oid, flags, err))
-		return -1;
+		return REF_TRANSACTION_ERROR_GENERIC;
 
 	if (flags & ~REF_TRANSACTION_UPDATE_ALLOWED_FLAGS)
 		BUG("illegal flags 0x%x passed to ref_transaction_update()", flags);
@@ -1416,8 +1421,32 @@ int ref_transaction_update(struct ref_transaction *transaction,
 	flags |= (new_oid ? REF_HAVE_NEW : 0) | (old_oid ? REF_HAVE_OLD : 0);
 	flags |= (new_target ? REF_HAVE_NEW : 0) | (old_target ? REF_HAVE_OLD : 0);
 
+	if ((flags & REF_HAVE_NEW) && !new_target && !is_null_oid(new_oid) &&
+	    !(flags & REF_SKIP_OID_VERIFICATION) && !(flags & REF_LOG_ONLY)) {
+		struct object *o = parse_object(transaction->ref_store->repo, new_oid);
+
+		if (!o) {
+			strbuf_addf(err,
+				    _("trying to write ref '%s' with nonexistent object %s"),
+				    refname, oid_to_hex(new_oid));
+			return REF_TRANSACTION_ERROR_INVALID_NEW_VALUE;
+		}
+
+		if (o->type != OBJ_COMMIT && is_branch(refname)) {
+			strbuf_addf(err, _("trying to write non-commit object %s to branch '%s'"),
+				    oid_to_hex(new_oid), refname);
+			return REF_TRANSACTION_ERROR_INVALID_NEW_VALUE;
+		}
+
+		if (o->type == OBJ_TAG) {
+			if (!peel_object(transaction->ref_store->repo, new_oid, &peeled,
+					 PEEL_OBJECT_VERIFY_TAGGED_OBJECT_TYPE))
+				flags |= REF_HAVE_PEELED;
+		}
+	}
+
 	ref_transaction_add_update(transaction, refname, flags,
-				   new_oid, old_oid, new_target,
+				   new_oid, old_oid, &peeled, new_target,
 				   old_target, NULL, msg);
 
 	return 0;
@@ -1444,7 +1473,7 @@ int ref_transaction_update_reflog(struct ref_transaction *transaction,
 		return -1;
 
 	update = ref_transaction_add_update(transaction, refname, flags,
-					    new_oid, old_oid, NULL, NULL,
+					    new_oid, old_oid, NULL, NULL, NULL,
 					    committer_info, msg);
 	update->index = index;
 
@@ -1472,7 +1501,7 @@ int ref_transaction_create(struct ref_transaction *transaction,
 		return 1;
 	}
 	return ref_transaction_update(transaction, refname, new_oid,
-				      null_oid(the_hash_algo), new_target, NULL, flags,
+				      null_oid(transaction->ref_store->repo->hash_algo), new_target, NULL, flags,
 				      msg, err);
 }
 
@@ -1491,7 +1520,7 @@ int ref_transaction_delete(struct ref_transaction *transaction,
 	if (old_target && !(flags & REF_NO_DEREF))
 		BUG("delete cannot operate on symrefs with deref mode");
 	return ref_transaction_update(transaction, refname,
-				      null_oid(the_hash_algo), old_oid,
+				      null_oid(transaction->ref_store->repo->hash_algo), old_oid,
 				      NULL, old_target, flags,
 				      msg, err);
 }
@@ -2295,6 +2324,10 @@ static struct ref_store *ref_store_init(struct repository *repo,
 {
 	const struct ref_storage_be *be;
 	struct ref_store *refs;
+	struct ref_store_init_options opts = {
+		.access_flags = flags,
+		.log_all_ref_updates = repo_settings_get_log_all_ref_updates(repo),
+	};
 
 	be = find_ref_storage_backend(format);
 	if (!be)
@@ -2304,7 +2337,8 @@ static struct ref_store *ref_store_init(struct repository *repo,
 	 * TODO Send in a 'struct worktree' instead of a 'gitdir', and
 	 * allow the backend to handle how it wants to deal with worktrees.
 	 */
-	refs = be->init(repo, repo->ref_storage_payload, gitdir, flags);
+	refs = be->init(repo, repo->ref_storage_payload, gitdir, &opts);
+
 	return refs;
 }
 
@@ -2379,7 +2413,7 @@ struct ref_store *repo_get_submodule_ref_store(struct repository *repo,
 	subrepo = xmalloc(sizeof(*subrepo));
 
 	if (repo_submodule_init(subrepo, repo, submodule,
-				null_oid(the_hash_algo))) {
+				null_oid(repo->hash_algo))) {
 		free(subrepo);
 		goto done;
 	}
@@ -2571,14 +2605,14 @@ static int transaction_hook_feed_stdin(int hook_stdin_fd, void *pp_cb, void *pp_
 	strbuf_reset(buf);
 
 	if (!(update->flags & REF_HAVE_OLD))
-		strbuf_addf(buf, "%s ", oid_to_hex(null_oid(the_hash_algo)));
+		strbuf_addf(buf, "%s ", oid_to_hex(null_oid(transaction->ref_store->repo->hash_algo)));
 	else if (update->old_target)
 		strbuf_addf(buf, "ref:%s ", update->old_target);
 	else
 		strbuf_addf(buf, "%s ", oid_to_hex(&update->old_oid));
 
 	if (!(update->flags & REF_HAVE_NEW))
-		strbuf_addf(buf, "%s ", oid_to_hex(null_oid(the_hash_algo)));
+		strbuf_addf(buf, "%s ", oid_to_hex(null_oid(transaction->ref_store->repo->hash_algo)));
 	else if (update->new_target)
 		strbuf_addf(buf, "ref:%s ", update->new_target);
 	else
@@ -3146,6 +3180,7 @@ struct migration_data {
 static int migrate_one_ref(const struct reference *ref, void *cb_data)
 {
 	struct migration_data *data = cb_data;
+	const struct git_hash_algo *hash_algo = data->transaction->ref_store->repo->hash_algo;
 	struct strbuf symref_target = STRBUF_INIT;
 	int ret;
 
@@ -3154,7 +3189,7 @@ static int migrate_one_ref(const struct reference *ref, void *cb_data)
 		if (ret < 0)
 			goto done;
 
-		ret = ref_transaction_update(data->transaction, ref->name, NULL, null_oid(the_hash_algo),
+		ret = ref_transaction_update(data->transaction, ref->name, NULL, null_oid(hash_algo),
 					     symref_target.buf, NULL,
 					     REF_SKIP_CREATE_REFLOG | REF_NO_DEREF, NULL, data->errbuf);
 		if (ret < 0)
@@ -3452,7 +3487,7 @@ int repo_migrate_ref_storage_format(struct repository *repo,
 	 * repository format so that clients will use the new ref store.
 	 * We also need to swap out the repository's main ref store.
 	 */
-	initialize_repository_version(hash_algo_by_ptr(repo->hash_algo), format, 1);
+	initialize_repository_version(the_repository, hash_algo_by_ptr(repo->hash_algo), format, 1);
 
 	/*
 	 * Unset the old ref store and release it. `get_main_ref_store()` will
diff --git a/refs.h b/refs.h
index d65de6a..71d5c18 100644
--- a/refs.h
+++ b/refs.h
@@ -905,14 +905,14 @@ struct ref_transaction *ref_store_transaction_begin(struct ref_store *refs,
  * See the above comment "Reference transaction updates" for more
  * information.
  */
-int ref_transaction_update(struct ref_transaction *transaction,
-			   const char *refname,
-			   const struct object_id *new_oid,
-			   const struct object_id *old_oid,
-			   const char *new_target,
-			   const char *old_target,
-			   unsigned int flags, const char *msg,
-			   struct strbuf *err);
+enum ref_transaction_error ref_transaction_update(struct ref_transaction *transaction,
+						  const char *refname,
+						  const struct object_id *new_oid,
+						  const struct object_id *old_oid,
+						  const char *new_target,
+						  const char *old_target,
+						  unsigned int flags, const char *msg,
+						  struct strbuf *err);
 
 /*
  * Similar to `ref_transaction_update`, but this function is only for adding
diff --git a/refs/files-backend.c b/refs/files-backend.c
index 0537a72..a4c7858 100644
--- a/refs/files-backend.c
+++ b/refs/files-backend.c
@@ -19,7 +19,6 @@
 #include "../iterator.h"
 #include "../dir-iterator.h"
 #include "../lockfile.h"
-#include "../object.h"
 #include "../path.h"
 #include "../dir.h"
 #include "../chdir-notify.h"
@@ -108,7 +107,7 @@ static void clear_loose_ref_cache(struct files_ref_store *refs)
 static struct ref_store *files_ref_store_init(struct repository *repo,
 					      const char *payload,
 					      const char *gitdir,
-					      unsigned int flags)
+					      const struct ref_store_init_options *opts)
 {
 	struct files_ref_store *refs = xcalloc(1, sizeof(*refs));
 	struct ref_store *ref_store = (struct ref_store *)refs;
@@ -120,11 +119,13 @@ static struct ref_store *files_ref_store_init(struct repository *repo,
 					 &ref_common_dir);
 
 	base_ref_store_init(ref_store, repo, refdir.buf, &refs_be_files);
-	refs->store_flags = flags;
+
 	refs->gitcommondir = strbuf_detach(&ref_common_dir, NULL);
 	refs->packed_ref_store =
-		packed_ref_store_init(repo, NULL, refs->gitcommondir, flags);
-	refs->log_all_ref_updates = repo_settings_get_log_all_ref_updates(repo);
+		packed_ref_store_init(repo, NULL, refs->gitcommondir, opts);
+	refs->store_flags = opts->access_flags;
+	refs->log_all_ref_updates = opts->log_all_ref_updates;
+
 	repo_config_get_bool(repo, "core.prefersymlinkrefs", &refs->prefer_symlink_refs);
 
 	chdir_notify_reparent("files-backend $GIT_DIR", &refs->base.gitdir);
@@ -792,7 +793,7 @@ static enum ref_transaction_error lock_raw_ref(struct files_ref_store *refs,
 
 	if (hold_lock_file_for_update_timeout(
 			    &lock->lk, ref_file.buf, LOCK_NO_DEREF,
-			    get_files_ref_lock_timeout_ms()) < 0) {
+			    get_files_ref_lock_timeout_ms(transaction->ref_store->repo)) < 0) {
 		int myerr = errno;
 		errno = 0;
 		if (myerr == ENOENT && --attempts_remaining > 0) {
@@ -1190,13 +1191,17 @@ static int remove_empty_directories(struct strbuf *path)
 	return remove_dir_recursively(path, REMOVE_DIR_EMPTY_ONLY);
 }
 
+struct create_reflock_cb {
+    struct lock_file *lk;
+    struct repository *repo;
+};
+
 static int create_reflock(const char *path, void *cb)
 {
-	struct lock_file *lk = cb;
-
+	struct create_reflock_cb *data = cb;
 	return hold_lock_file_for_update_timeout(
-			lk, path, LOCK_NO_DEREF,
-			get_files_ref_lock_timeout_ms()) < 0 ? -1 : 0;
+			data->lk, path, LOCK_NO_DEREF,
+			get_files_ref_lock_timeout_ms(data->repo)) < 0 ? -1 : 0;
 }
 
 /*
@@ -1208,6 +1213,7 @@ static struct ref_lock *lock_ref_oid_basic(struct files_ref_store *refs,
 {
 	struct strbuf ref_file = STRBUF_INIT;
 	struct ref_lock *lock;
+	struct create_reflock_cb cb_data;
 
 	files_assert_main_repository(refs, "lock_ref_oid_basic");
 	assert(err);
@@ -1229,8 +1235,10 @@ static struct ref_lock *lock_ref_oid_basic(struct files_ref_store *refs,
 
 	lock->ref_name = xstrdup(refname);
 	lock->count = 1;
+	cb_data.lk   = &lock->lk;
+	cb_data.repo = refs->base.repo;
 
-	if (raceproof_create_file(ref_file.buf, create_reflock, &lock->lk)) {
+	if (raceproof_create_file(ref_file.buf, create_reflock, &cb_data)) {
 		unable_to_lock_message(ref_file.buf, errno, err);
 		goto error_return;
 	}
@@ -1324,7 +1332,8 @@ static void prune_ref(struct files_ref_store *refs, struct ref_to_prune *r)
 	ref_transaction_add_update(
 			transaction, r->name,
 			REF_NO_DEREF | REF_HAVE_NEW | REF_HAVE_OLD | REF_IS_PRUNING,
-			null_oid(the_hash_algo), &r->oid, NULL, NULL, NULL, NULL);
+			null_oid(the_hash_algo), &r->oid, NULL, NULL, NULL,
+			NULL, NULL);
 	if (ref_transaction_commit(transaction, &err))
 		goto cleanup;
 
@@ -1587,7 +1596,6 @@ static int rename_tmp_log(struct files_ref_store *refs, const char *newrefname)
 static enum ref_transaction_error write_ref_to_lockfile(struct files_ref_store *refs,
 							struct ref_lock *lock,
 							const struct object_id *oid,
-							int skip_oid_verification,
 							struct strbuf *err);
 static int commit_ref_update(struct files_ref_store *refs,
 			     struct ref_lock *lock,
@@ -1735,7 +1743,7 @@ static int files_copy_or_rename_ref(struct ref_store *ref_store,
 	}
 	oidcpy(&lock->old_oid, &orig_oid);
 
-	if (write_ref_to_lockfile(refs, lock, &orig_oid, 0, &err) ||
+	if (write_ref_to_lockfile(refs, lock, &orig_oid, &err) ||
 	    commit_ref_update(refs, lock, &orig_oid, logmsg, 0, &err)) {
 		error("unable to write current sha1 into %s: %s", newrefname, err.buf);
 		strbuf_release(&err);
@@ -1753,7 +1761,7 @@ static int files_copy_or_rename_ref(struct ref_store *ref_store,
 		goto rollbacklog;
 	}
 
-	if (write_ref_to_lockfile(refs, lock, &orig_oid, 0, &err) ||
+	if (write_ref_to_lockfile(refs, lock, &orig_oid, &err) ||
 	    commit_ref_update(refs, lock, &orig_oid, NULL, REF_SKIP_CREATE_REFLOG, &err)) {
 		error("unable to write current sha1 into %s: %s", oldrefname, err.buf);
 		strbuf_release(&err);
@@ -1997,32 +2005,11 @@ static int files_log_ref_write(struct files_ref_store *refs,
 static enum ref_transaction_error write_ref_to_lockfile(struct files_ref_store *refs,
 							struct ref_lock *lock,
 							const struct object_id *oid,
-							int skip_oid_verification,
 							struct strbuf *err)
 {
 	static char term = '\n';
-	struct object *o;
 	int fd;
 
-	if (!skip_oid_verification) {
-		o = parse_object(refs->base.repo, oid);
-		if (!o) {
-			strbuf_addf(
-				err,
-				"trying to write ref '%s' with nonexistent object %s",
-				lock->ref_name, oid_to_hex(oid));
-			unlock_ref(lock);
-			return REF_TRANSACTION_ERROR_INVALID_NEW_VALUE;
-		}
-		if (o->type != OBJ_COMMIT && is_branch(lock->ref_name)) {
-			strbuf_addf(
-				err,
-				"trying to write non-commit object %s to branch '%s'",
-				oid_to_hex(oid), lock->ref_name);
-			unlock_ref(lock);
-			return REF_TRANSACTION_ERROR_INVALID_NEW_VALUE;
-		}
-	}
 	fd = get_lock_file_fd(&lock->lk);
 	if (write_in_full(fd, oid_to_hex(oid), refs->base.repo->hash_algo->hexsz) < 0 ||
 	    write_in_full(fd, &term, 1) < 0 ||
@@ -2190,7 +2177,7 @@ static int show_one_reflog_ent(struct files_ref_store *refs,
 	char *email_end, *message;
 	timestamp_t timestamp;
 	int tz;
-	const char *p = sb->buf;
+	char *p = sb->buf;
 
 	/* old SP new SP name <email> SP time TAB msg LF */
 	if (!sb->len || sb->buf[sb->len - 1] != '\n' ||
@@ -2489,7 +2476,7 @@ static enum ref_transaction_error split_head_update(struct ref_update *update,
 	new_update = ref_transaction_add_update(
 			transaction, "HEAD",
 			update->flags | REF_LOG_ONLY | REF_NO_DEREF | REF_LOG_VIA_SPLIT,
-			&update->new_oid, &update->old_oid,
+			&update->new_oid, &update->old_oid, &update->peeled,
 			NULL, NULL, update->committer_info, update->msg);
 	new_update->parent_update = update;
 
@@ -2551,8 +2538,8 @@ static enum ref_transaction_error split_symref_update(struct ref_update *update,
 			transaction, referent, new_flags,
 			update->new_target ? NULL : &update->new_oid,
 			update->old_target ? NULL : &update->old_oid,
-			update->new_target, update->old_target, NULL,
-			update->msg);
+			&update->peeled, update->new_target, update->old_target,
+			NULL, update->msg);
 
 	new_update->parent_update = update;
 
@@ -2826,7 +2813,6 @@ static enum ref_transaction_error lock_ref_for_update(struct files_ref_store *re
 		} else {
 			ret = write_ref_to_lockfile(
 				refs, lock, &update->new_oid,
-				update->flags & REF_SKIP_OID_VERIFICATION,
 				err);
 			if (ret) {
 				char *write_err = strbuf_detach(err, NULL);
@@ -3016,7 +3002,7 @@ static int files_transaction_prepare(struct ref_store *ref_store,
 			ref_transaction_add_update(
 					packed_transaction, update->refname,
 					REF_HAVE_NEW | REF_NO_DEREF,
-					&update->new_oid, NULL,
+					&update->new_oid, NULL, NULL,
 					NULL, NULL, NULL, NULL);
 		}
 	}
@@ -3222,19 +3208,22 @@ static int files_transaction_finish_initial(struct files_ref_store *refs,
 			if (update->flags & REF_LOG_ONLY)
 				ref_transaction_add_update(loose_transaction, update->refname,
 							   update->flags, &update->new_oid,
-							   &update->old_oid, NULL, NULL,
+							   &update->old_oid, &update->peeled,
+							   NULL, NULL,
 							   update->committer_info, update->msg);
 			else
 				ref_transaction_add_update(loose_transaction, update->refname,
 							   update->flags & ~REF_HAVE_OLD,
 							   update->new_target ? NULL : &update->new_oid, NULL,
-							   update->new_target, NULL, update->committer_info,
+							   &update->peeled, update->new_target,
+							   NULL, update->committer_info,
 							   NULL);
 		} else {
 			ref_transaction_add_update(packed_transaction, update->refname,
 						   update->flags & ~REF_HAVE_OLD,
 						   &update->new_oid, &update->old_oid,
-						   NULL, NULL, update->committer_info, NULL);
+						   &update->peeled, NULL, NULL,
+						   update->committer_info, NULL);
 		}
 	}
 
@@ -3864,22 +3853,12 @@ static int files_fsck_refs_content(struct ref_store *ref_store,
 static int files_fsck_refs_name(struct ref_store *ref_store UNUSED,
 				struct fsck_options *o,
 				const char *refname,
-				const char *path,
+				const char *path UNUSED,
 				int mode UNUSED)
 {
 	struct strbuf sb = STRBUF_INIT;
-	const char *filename;
 	int ret = 0;
 
-	filename = basename((char *) path);
-
-	/*
-	 * Ignore the files ending with ".lock" as they may be lock files
-	 * However, do not allow bare ".lock" files.
-	 */
-	if (filename[0] != '.' && ends_with(filename, ".lock"))
-		goto cleanup;
-
 	if (is_root_ref(refname))
 		goto cleanup;
 
@@ -3939,6 +3918,7 @@ static int files_fsck_refs_dir(struct ref_store *ref_store,
 	struct strbuf refname = STRBUF_INIT;
 	struct strbuf sb = STRBUF_INIT;
 	struct dir_iterator *iter;
+	const char *filename;
 	int iter_status;
 	int ret = 0;
 
@@ -3962,6 +3942,15 @@ static int files_fsck_refs_dir(struct ref_store *ref_store,
 			strbuf_addf(&refname, "worktrees/%s/", wt->id);
 		strbuf_addf(&refname, "refs/%s", iter->relative_path);
 
+		filename = basename((char *) iter->path.buf);
+
+		/*
+		 * Ignore the files ending with ".lock" as they may be lock files.
+		 * However, do not skip invalid refnames with '.lock' suffix.
+		 */
+		if (filename[0] != '.' && ends_with(filename, ".lock"))
+			continue;
+
 		if (files_fsck_ref(ref_store, o, refname.buf,
 				   iter->path.buf, iter->st.st_mode) < 0)
 			ret = -1;
diff --git a/refs/packed-backend.c b/refs/packed-backend.c
index 23ed629..0acde48 100644
--- a/refs/packed-backend.c
+++ b/refs/packed-backend.c
@@ -218,14 +218,14 @@ static size_t snapshot_hexsz(const struct snapshot *snapshot)
 struct ref_store *packed_ref_store_init(struct repository *repo,
 					const char *payload UNUSED,
 					const char *gitdir,
-					unsigned int store_flags)
+					const struct ref_store_init_options *opts)
 {
 	struct packed_ref_store *refs = xcalloc(1, sizeof(*refs));
 	struct ref_store *ref_store = (struct ref_store *)refs;
 	struct strbuf sb = STRBUF_INIT;
 
 	base_ref_store_init(ref_store, repo, gitdir, &refs_be_packed);
-	refs->store_flags = store_flags;
+	refs->store_flags = opts->access_flags;
 
 	strbuf_addf(&sb, "%s/packed-refs", gitdir);
 	refs->path = strbuf_detach(&sb, NULL);
@@ -1531,13 +1531,11 @@ static enum ref_transaction_error write_with_updates(struct packed_ref_store *re
 			 */
 			i++;
 		} else {
-			struct object_id peeled;
-			int peel_error = peel_object(refs->base.repo, &update->new_oid,
-						     &peeled, PEEL_OBJECT_VERIFY_TAGGED_OBJECT_TYPE);
+			bool peeled = update->flags & REF_HAVE_PEELED;
 
 			if (write_packed_entry(out, update->refname,
 					       &update->new_oid,
-					       peel_error ? NULL : &peeled))
+					       peeled ? &update->peeled : NULL))
 				goto write_error;
 
 			i++;
diff --git a/refs/packed-backend.h b/refs/packed-backend.h
index 2c2377a..1db48e8 100644
--- a/refs/packed-backend.h
+++ b/refs/packed-backend.h
@@ -3,6 +3,7 @@
 
 struct repository;
 struct ref_transaction;
+struct ref_store_init_options;
 
 /*
  * Support for storing references in a `packed-refs` file.
@@ -16,7 +17,7 @@ struct ref_transaction;
 struct ref_store *packed_ref_store_init(struct repository *repo,
 					const char *payload,
 					const char *gitdir,
-					unsigned int store_flags);
+					const struct ref_store_init_options *options);
 
 /*
  * Lock the packed-refs file for writing. Flags is passed to
diff --git a/refs/refs-internal.h b/refs/refs-internal.h
index d79e35f..a08d589 100644
--- a/refs/refs-internal.h
+++ b/refs/refs-internal.h
@@ -40,10 +40,17 @@ struct ref_transaction;
 #define REF_LOG_ONLY (1 << 7)
 
 /*
+ * The reference contains a peeled object ID. This is used when the
+ * new_oid is pointing to a tag object and the reference backend
+ * wants to also store the peeled value for optimized retrieval.
+ */
+#define REF_HAVE_PEELED (1 << 15)
+
+/*
  * Return the length of time to retry acquiring a loose reference lock
  * before giving up, in milliseconds:
  */
-long get_files_ref_lock_timeout_ms(void);
+long get_files_ref_lock_timeout_ms(struct repository *repo);
 
 /*
  * Return true iff refname is minimally safe. "Safe" here means that
@@ -93,6 +100,12 @@ struct ref_update {
 	struct object_id old_oid;
 
 	/*
+	 * If the new_oid points to a tag object, set this to the peeled
+	 * object ID for optimized retrieval without needed to hit the odb.
+	 */
+	struct object_id peeled;
+
+	/*
 	 * If set, point the reference to this value. This can also be
 	 * used to convert regular references to become symbolic refs.
 	 * Cannot be set together with `new_oid`.
@@ -169,6 +182,7 @@ struct ref_update *ref_transaction_add_update(
 		const char *refname, unsigned int flags,
 		const struct object_id *new_oid,
 		const struct object_id *old_oid,
+		const struct object_id *peeled,
 		const char *new_target, const char *old_target,
 		const char *committer_info,
 		const char *msg);
@@ -386,6 +400,21 @@ struct ref_store;
 				 REF_STORE_MAIN)
 
 /*
+ * Options for initializing the ref backend. All backend-agnostic information
+ * which backends required will be held here.
+ */
+struct ref_store_init_options {
+	/* The kind of operations that the ref_store is allowed to perform. */
+	unsigned int access_flags;
+
+	/*
+	 * Denotes under what conditions reflogs should be created when updating
+	 * references.
+	 */
+	enum log_refs_config log_all_ref_updates;
+};
+
+/*
  * Initialize the ref_store for the specified gitdir. These functions
  * should call base_ref_store_init() to initialize the shared part of
  * the ref_store and to record the ref_store for later lookup.
@@ -393,7 +422,7 @@ struct ref_store;
 typedef struct ref_store *ref_store_init_fn(struct repository *repo,
 					    const char *payload,
 					    const char *gitdir,
-					    unsigned int flags);
+					    const struct ref_store_init_options *opts);
 /*
  * Release all memory and resources associated with the ref store.
  */
@@ -421,10 +450,6 @@ typedef int ref_transaction_abort_fn(struct ref_store *refs,
 				     struct ref_transaction *transaction,
 				     struct strbuf *err);
 
-typedef int ref_transaction_commit_fn(struct ref_store *refs,
-				      struct ref_transaction *transaction,
-				      struct strbuf *err);
-
 typedef int optimize_fn(struct ref_store *ref_store,
 			struct refs_optimize_opts *opts);
 
diff --git a/refs/reftable-backend.c b/refs/reftable-backend.c
index b124404..4ae2292 100644
--- a/refs/reftable-backend.c
+++ b/refs/reftable-backend.c
@@ -12,7 +12,6 @@
 #include "../hex.h"
 #include "../ident.h"
 #include "../iterator.h"
-#include "../object.h"
 #include "../parse.h"
 #include "../path.h"
 #include "../refs.h"
@@ -366,15 +365,10 @@ static int reftable_be_config(const char *var, const char *value,
 	return 0;
 }
 
-static int reftable_be_fsync(int fd)
-{
-	return fsync_component(FSYNC_COMPONENT_REFERENCE, fd);
-}
-
 static struct ref_store *reftable_be_init(struct repository *repo,
 					  const char *payload,
 					  const char *gitdir,
-					  unsigned int store_flags)
+					  const struct ref_store_init_options *opts)
 {
 	struct reftable_ref_store *refs = xcalloc(1, sizeof(*refs));
 	struct strbuf ref_common_dir = STRBUF_INIT;
@@ -391,8 +385,8 @@ static struct ref_store *reftable_be_init(struct repository *repo,
 
 	base_ref_store_init(&refs->base, repo, refdir.buf, &refs_be_reftable);
 	strmap_init(&refs->worktree_backends);
-	refs->store_flags = store_flags;
-	refs->log_all_ref_updates = repo_settings_get_log_all_ref_updates(repo);
+	refs->log_all_ref_updates = opts->log_all_ref_updates;
+	refs->store_flags = opts->access_flags;
 
 	switch (repo->hash_algo->format_id) {
 	case GIT_SHA1_FORMAT_ID:
@@ -404,13 +398,12 @@ static struct ref_store *reftable_be_init(struct repository *repo,
 	default:
 		BUG("unknown hash algorithm %d", repo->hash_algo->format_id);
 	}
-	refs->write_options.default_permissions = calc_shared_perm(the_repository, 0666 & ~mask);
+	refs->write_options.default_permissions = calc_shared_perm(repo, 0666 & ~mask);
 	refs->write_options.disable_auto_compact =
 		!git_env_bool("GIT_TEST_REFTABLE_AUTOCOMPACTION", 1);
 	refs->write_options.lock_timeout_ms = 100;
-	refs->write_options.fsync = reftable_be_fsync;
 
-	repo_config(the_repository, reftable_be_config, &refs->write_options);
+	repo_config(repo, reftable_be_config, &refs->write_options);
 
 	/*
 	 * It is somewhat unfortunate that we have to mirror the default block
@@ -492,7 +485,7 @@ static int reftable_be_create_on_disk(struct ref_store *ref_store,
 	struct strbuf sb = STRBUF_INIT;
 
 	strbuf_addf(&sb, "%s/reftable", refs->base.gitdir);
-	safe_create_dir(the_repository, sb.buf, 1);
+	safe_create_dir(ref_store->repo, sb.buf, 1);
 	strbuf_reset(&sb);
 
 	strbuf_release(&sb);
@@ -1087,25 +1080,6 @@ static enum ref_transaction_error prepare_single_update(struct reftable_ref_stor
 		return 0;
 	}
 
-	/* Verify that the new object ID is valid. */
-	if ((u->flags & REF_HAVE_NEW) && !is_null_oid(&u->new_oid) &&
-	    !(u->flags & REF_SKIP_OID_VERIFICATION) &&
-	    !(u->flags & REF_LOG_ONLY)) {
-		struct object *o = parse_object(refs->base.repo, &u->new_oid);
-		if (!o) {
-			strbuf_addf(err,
-				    _("trying to write ref '%s' with nonexistent object %s"),
-				    u->refname, oid_to_hex(&u->new_oid));
-			return REF_TRANSACTION_ERROR_INVALID_NEW_VALUE;
-		}
-
-		if (o->type != OBJ_COMMIT && is_branch(u->refname)) {
-			strbuf_addf(err, _("trying to write non-commit object %s to branch '%s'"),
-				    oid_to_hex(&u->new_oid), u->refname);
-			return REF_TRANSACTION_ERROR_INVALID_NEW_VALUE;
-		}
-	}
-
 	/*
 	 * When we update the reference that HEAD points to we enqueue
 	 * a second log-only update for HEAD so that its reflog is
@@ -1132,8 +1106,8 @@ static enum ref_transaction_error prepare_single_update(struct reftable_ref_stor
 		ref_transaction_add_update(
 			transaction, "HEAD",
 			u->flags | REF_LOG_ONLY | REF_NO_DEREF,
-			&u->new_oid, &u->old_oid, NULL, NULL, NULL,
-			u->msg);
+			&u->new_oid, &u->old_oid, &u->peeled, NULL, NULL,
+			NULL, u->msg);
 	}
 
 	ret = reftable_backend_read_ref(be, rewritten_ref,
@@ -1219,7 +1193,7 @@ static enum ref_transaction_error prepare_single_update(struct reftable_ref_stor
 				transaction, referent->buf, new_flags,
 				u->new_target ? NULL : &u->new_oid,
 				u->old_target ? NULL : &u->old_oid,
-				u->new_target, u->old_target,
+				&u->peeled, u->new_target, u->old_target,
 				u->committer_info, u->msg);
 
 			new_update->parent_update = u;
@@ -1609,17 +1583,13 @@ static int write_transaction_table(struct reftable_writer *writer, void *cb_data
 				goto done;
 		} else if (u->flags & REF_HAVE_NEW) {
 			struct reftable_ref_record ref = {0};
-			struct object_id peeled;
-			int peel_error;
 
 			ref.refname = (char *)u->refname;
 			ref.update_index = ts;
 
-			peel_error = peel_object(arg->refs->base.repo, &u->new_oid, &peeled,
-						 PEEL_OBJECT_VERIFY_TAGGED_OBJECT_TYPE);
-			if (!peel_error) {
+			if (u->flags & REF_HAVE_PEELED) {
 				ref.value_type = REFTABLE_REF_VAL2;
-				memcpy(ref.value.val2.target_value, peeled.hash, GIT_MAX_RAWSZ);
+				memcpy(ref.value.val2.target_value, u->peeled.hash, GIT_MAX_RAWSZ);
 				memcpy(ref.value.val2.value, u->new_oid.hash, GIT_MAX_RAWSZ);
 			} else if (!is_null_oid(&u->new_oid)) {
 				ref.value_type = REFTABLE_REF_VAL1;
diff --git a/reftable/blocksource.c b/reftable/blocksource.c
index 573c812..7f7441f 100644
--- a/reftable/blocksource.c
+++ b/reftable/blocksource.c
@@ -93,13 +93,12 @@ void block_source_from_buf(struct reftable_block_source *bs,
 }
 
 struct file_block_source {
-	uint64_t size;
-	unsigned char *data;
+	struct reftable_mmap mmap;
 };
 
 static uint64_t file_size(void *b)
 {
-	return ((struct file_block_source *)b)->size;
+	return ((struct file_block_source *)b)->mmap.size;
 }
 
 static void file_release_data(void *b REFTABLE_UNUSED, struct reftable_block_data *dest REFTABLE_UNUSED)
@@ -109,7 +108,7 @@ static void file_release_data(void *b REFTABLE_UNUSED, struct reftable_block_dat
 static void file_close(void *v)
 {
 	struct file_block_source *b = v;
-	munmap(b->data, b->size);
+	reftable_munmap(&b->mmap);
 	reftable_free(b);
 }
 
@@ -117,8 +116,8 @@ static ssize_t file_read_data(void *v, struct reftable_block_data *dest, uint64_
 			      uint32_t size)
 {
 	struct file_block_source *b = v;
-	assert(off + size <= b->size);
-	dest->data = b->data + off;
+	assert(off + size <= b->mmap.size);
+	dest->data = (unsigned char *) b->mmap.data + off;
 	dest->len = size;
 	return size;
 }
@@ -156,13 +155,9 @@ int reftable_block_source_from_file(struct reftable_block_source *bs,
 		goto out;
 	}
 
-	p->size = st.st_size;
-	p->data = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
-	if (p->data == MAP_FAILED) {
-		err = REFTABLE_IO_ERROR;
-		p->data = NULL;
+	err = reftable_mmap(&p->mmap, fd, st.st_size);
+	if (err < 0)
 		goto out;
-	}
 
 	assert(!bs->ops);
 	bs->ops = &file_vtable;
diff --git a/reftable/fsck.c b/reftable/fsck.c
index 26b9115..8e73fc8 100644
--- a/reftable/fsck.c
+++ b/reftable/fsck.c
@@ -63,7 +63,7 @@ static int table_check_name(struct reftable_table *table,
 
 static int table_checks(struct reftable_table *table,
 			reftable_fsck_report_fn report_fn,
-			reftable_fsck_verbose_fn verbose_fn UNUSED,
+			reftable_fsck_verbose_fn verbose_fn REFTABLE_UNUSED,
 			void *cb_data)
 {
 	table_check_fn table_check_fns[] = {
diff --git a/reftable/reftable-basics.h b/reftable/reftable-basics.h
index 6d73f19..dc86226 100644
--- a/reftable/reftable-basics.h
+++ b/reftable/reftable-basics.h
@@ -9,7 +9,7 @@
 #ifndef REFTABLE_BASICS_H
 #define REFTABLE_BASICS_H
 
-#include <stddef.h>
+#include "reftable-system.h"
 
 /* A buffer that contains arbitrary byte slices. */
 struct reftable_buf {
diff --git a/reftable/reftable-block.h b/reftable/reftable-block.h
index 0b05a8f..94c79b5 100644
--- a/reftable/reftable-block.h
+++ b/reftable/reftable-block.h
@@ -9,8 +9,7 @@
 #ifndef REFTABLE_BLOCK_H
 #define REFTABLE_BLOCK_H
 
-#include <stdint.h>
-
+#include "reftable-system.h"
 #include "reftable-basics.h"
 #include "reftable-blocksource.h"
 #include "reftable-iterator.h"
diff --git a/reftable/reftable-blocksource.h b/reftable/reftable-blocksource.h
index f5ba867..40c1e94 100644
--- a/reftable/reftable-blocksource.h
+++ b/reftable/reftable-blocksource.h
@@ -9,7 +9,7 @@
 #ifndef REFTABLE_BLOCKSOURCE_H
 #define REFTABLE_BLOCKSOURCE_H
 
-#include <stdint.h>
+#include "reftable-system.h"
 
 /*
  * Generic wrapper for a seekable readable file.
diff --git a/reftable/reftable-error.h b/reftable/reftable-error.h
index d100e0d..0535e14 100644
--- a/reftable/reftable-error.h
+++ b/reftable/reftable-error.h
@@ -9,6 +9,8 @@
 #ifndef REFTABLE_ERROR_H
 #define REFTABLE_ERROR_H
 
+#include "reftable-system.h"
+
 /*
  * Errors in reftable calls are signaled with negative integer return values. 0
  * means success.
diff --git a/reftable/reftable-fsck.h b/reftable/reftable-fsck.h
index 007a392..340fc77 100644
--- a/reftable/reftable-fsck.h
+++ b/reftable/reftable-fsck.h
@@ -1,6 +1,7 @@
 #ifndef REFTABLE_FSCK_H
 #define REFTABLE_FSCK_H
 
+#include "reftable-system.h"
 #include "reftable-stack.h"
 
 enum reftable_fsck_error {
diff --git a/reftable/reftable-iterator.h b/reftable/reftable-iterator.h
index af58202..a050cc1 100644
--- a/reftable/reftable-iterator.h
+++ b/reftable/reftable-iterator.h
@@ -9,6 +9,7 @@
 #ifndef REFTABLE_ITERATOR_H
 #define REFTABLE_ITERATOR_H
 
+#include "reftable-system.h"
 #include "reftable-record.h"
 
 struct reftable_iterator_vtable;
diff --git a/reftable/reftable-merged.h b/reftable/reftable-merged.h
index e5af846..02a9966 100644
--- a/reftable/reftable-merged.h
+++ b/reftable/reftable-merged.h
@@ -9,6 +9,7 @@
 #ifndef REFTABLE_MERGED_H
 #define REFTABLE_MERGED_H
 
+#include "reftable-system.h"
 #include "reftable-iterator.h"
 
 /*
diff --git a/reftable/reftable-record.h b/reftable/reftable-record.h
index 385a74c..e18c538 100644
--- a/reftable/reftable-record.h
+++ b/reftable/reftable-record.h
@@ -9,8 +9,8 @@
 #ifndef REFTABLE_RECORD_H
 #define REFTABLE_RECORD_H
 
+#include "reftable-system.h"
 #include "reftable-basics.h"
-#include <stdint.h>
 
 /*
  * Basic data types
diff --git a/reftable/reftable-stack.h b/reftable/reftable-stack.h
index c2415cb..5f7be57 100644
--- a/reftable/reftable-stack.h
+++ b/reftable/reftable-stack.h
@@ -9,6 +9,7 @@
 #ifndef REFTABLE_STACK_H
 #define REFTABLE_STACK_H
 
+#include "reftable-system.h"
 #include "reftable-writer.h"
 
 /*
diff --git a/reftable/reftable-system.h b/reftable/reftable-system.h
new file mode 100644
index 0000000..76f3e33
--- /dev/null
+++ b/reftable/reftable-system.h
@@ -0,0 +1,18 @@
+#ifndef REFTABLE_SYSTEM_H
+#define REFTABLE_SYSTEM_H
+
+/*
+ * This header defines the platform-specific bits required to compile the
+ * reftable library. It should provide an environment that bridges over the
+ * gaps between POSIX and your system, as well as the zlib interfaces. This
+ * header is expected to be changed by the individual project.
+ */
+
+#define MINGW_DONT_HANDLE_IN_USE_ERROR
+#include "compat/posix.h"
+#include "compat/zlib-compat.h"
+
+int reftable_fsync(int fd);
+#define fsync(fd) reftable_fsync(fd)
+
+#endif
diff --git a/reftable/reftable-table.h b/reftable/reftable-table.h
index 5f935d0..d7666b5 100644
--- a/reftable/reftable-table.h
+++ b/reftable/reftable-table.h
@@ -9,6 +9,7 @@
 #ifndef REFTABLE_TABLE_H
 #define REFTABLE_TABLE_H
 
+#include "reftable-system.h"
 #include "reftable-iterator.h"
 #include "reftable-block.h"
 #include "reftable-blocksource.h"
diff --git a/reftable/reftable-writer.h b/reftable/reftable-writer.h
index 1e7003c..a66db41 100644
--- a/reftable/reftable-writer.h
+++ b/reftable/reftable-writer.h
@@ -9,11 +9,9 @@
 #ifndef REFTABLE_WRITER_H
 #define REFTABLE_WRITER_H
 
+#include "reftable-system.h"
 #include "reftable-record.h"
 
-#include <stdint.h>
-#include <unistd.h> /* ssize_t */
-
 /* Writing single reftables */
 
 /* reftable_write_options sets options for writing a single reftable. */
@@ -64,12 +62,6 @@ struct reftable_write_options {
 	long lock_timeout_ms;
 
 	/*
-	 * Optional callback used to fsync files to disk. Falls back to using
-	 * fsync(3P) when unset.
-	 */
-	int (*fsync)(int fd);
-
-	/*
 	 * Callback function to execute whenever the stack is being reloaded.
 	 * This can be used e.g. to discard cached information that relies on
 	 * the old stack's data. The payload data will be passed as argument to
diff --git a/reftable/stack.c b/reftable/stack.c
index 1c9f21d..1fba96d 100644
--- a/reftable/stack.c
+++ b/reftable/stack.c
@@ -29,13 +29,6 @@ static int stack_filename(struct reftable_buf *dest, struct reftable_stack *st,
 	return 0;
 }
 
-static int stack_fsync(const struct reftable_write_options *opts, int fd)
-{
-	if (opts->fsync)
-		return opts->fsync(fd);
-	return fsync(fd);
-}
-
 static ssize_t reftable_write_data(int fd, const void *data, size_t size)
 {
 	size_t total_written = 0;
@@ -69,7 +62,7 @@ static ssize_t fd_writer_write(void *arg, const void *data, size_t sz)
 static int fd_writer_flush(void *arg)
 {
 	struct fd_writer *writer = arg;
-	return stack_fsync(writer->opts, writer->fd);
+	return fsync(writer->fd);
 }
 
 static int fd_read_lines(int fd, char ***namesp)
@@ -372,45 +365,26 @@ static int reftable_stack_reload_once(struct reftable_stack *st,
 	return err;
 }
 
-/* return negative if a before b. */
-static int tv_cmp(struct timeval *a, struct timeval *b)
-{
-	time_t diff = a->tv_sec - b->tv_sec;
-	int udiff = a->tv_usec - b->tv_usec;
-
-	if (diff != 0)
-		return diff;
-
-	return udiff;
-}
-
 static int reftable_stack_reload_maybe_reuse(struct reftable_stack *st,
 					     int reuse_open)
 {
 	char **names = NULL, **names_after = NULL;
-	struct timeval deadline;
+	uint64_t deadline;
 	int64_t delay = 0;
 	int tries = 0, err;
 	int fd = -1;
 
-	err = gettimeofday(&deadline, NULL);
-	if (err < 0)
-		goto out;
-	deadline.tv_sec += 3;
+	deadline = reftable_time_ms() + 3000;
 
 	while (1) {
-		struct timeval now;
-
-		err = gettimeofday(&now, NULL);
-		if (err < 0)
-			goto out;
+		uint64_t now = reftable_time_ms();
 
 		/*
 		 * Only look at deadlines after the first few times. This
 		 * simplifies debugging in GDB.
 		 */
 		tries++;
-		if (tries > 3 && tv_cmp(&now, &deadline) >= 0)
+		if (tries > 3 && now >= deadline)
 			goto out;
 
 		fd = open(st->list_file, O_RDONLY);
@@ -812,7 +786,7 @@ int reftable_addition_commit(struct reftable_addition *add)
 		goto done;
 	}
 
-	err = stack_fsync(&add->stack->opts, add->tables_list_lock.fd);
+	err = fsync(add->tables_list_lock.fd);
 	if (err < 0) {
 		err = REFTABLE_IO_ERROR;
 		goto done;
@@ -1480,7 +1454,7 @@ static int stack_compact_range(struct reftable_stack *st,
 		goto done;
 	}
 
-	err = stack_fsync(&st->opts, tables_list_lock.fd);
+	err = fsync(tables_list_lock.fd);
 	if (err < 0) {
 		err = REFTABLE_IO_ERROR;
 		unlink(new_table_path.buf);
diff --git a/reftable/system.c b/reftable/system.c
index 725a258..9063641 100644
--- a/reftable/system.c
+++ b/reftable/system.c
@@ -4,7 +4,9 @@
 #include "basics.h"
 #include "reftable-error.h"
 #include "../lockfile.h"
+#include "../trace.h"
 #include "../tempfile.h"
+#include "../write-or-die.h"
 
 uint32_t reftable_rand(void)
 {
@@ -131,3 +133,33 @@ int flock_commit(struct reftable_flock *l)
 
 	return 0;
 }
+
+int reftable_fsync(int fd)
+{
+	return fsync_component(FSYNC_COMPONENT_REFERENCE, fd);
+}
+
+uint64_t reftable_time_ms(void)
+{
+	return getnanotime() / 1000000;
+}
+
+int reftable_mmap(struct reftable_mmap *out, int fd, size_t len)
+{
+	void *data = xmmap_gently(NULL, len, PROT_READ, MAP_PRIVATE, fd, 0);
+	if (data == MAP_FAILED)
+		return REFTABLE_IO_ERROR;
+
+	out->data = data;
+	out->size = len;
+
+	return 0;
+}
+
+int reftable_munmap(struct reftable_mmap *mmap)
+{
+	if (munmap(mmap->data, mmap->size) < 0)
+		return REFTABLE_IO_ERROR;
+	memset(mmap, 0, sizeof(*mmap));
+	return 0;
+}
diff --git a/reftable/system.h b/reftable/system.h
index c54ed4c..c0e2cbe 100644
--- a/reftable/system.h
+++ b/reftable/system.h
@@ -9,11 +9,14 @@
 #ifndef SYSTEM_H
 #define SYSTEM_H
 
-/* This header glues the reftable library to the rest of Git */
+/*
+ * This header defines the platform-agnostic interface that is to be
+ * implemented by the project to make it work on their respective supported
+ * systems, and to integrate it into the project itself. This header is not
+ * expected to be changed by the individual project.
+ */
 
-#define MINGW_DONT_HANDLE_IN_USE_ERROR
-#include "compat/posix.h"
-#include "compat/zlib-compat.h"
+#include "reftable-system.h"
 
 /*
  * Return a random 32 bit integer. This function is expected to return
@@ -108,4 +111,25 @@ int flock_release(struct reftable_flock *l);
  */
 int flock_commit(struct reftable_flock *l);
 
+/* Report the time in milliseconds. */
+uint64_t reftable_time_ms(void);
+
+struct reftable_mmap {
+       void *data;
+       size_t size;
+       void *priv;
+};
+
+/*
+ * Map the file into memory. Returns 0 on success, a reftable error code on
+ * error.
+ */
+int reftable_mmap(struct reftable_mmap *out, int fd, size_t len);
+
+/*
+ * Unmap the file from memory. Returns 0 on success, a reftable error code on
+ * error.
+ */
+int reftable_munmap(struct reftable_mmap *mmap);
+
 #endif
diff --git a/remote-curl.c b/remote-curl.c
index aba60d5..9e614c5 100644
--- a/remote-curl.c
+++ b/remote-curl.c
@@ -946,7 +946,7 @@ static int post_rpc(struct rpc_state *rpc, int stateless_connect, int flush_rece
 		do {
 			err = probe_rpc(rpc, &results);
 			if (err == HTTP_REAUTH)
-				credential_fill(the_repository, &http_auth, 0);
+				http_reauth_prepare(0);
 		} while (err == HTTP_REAUTH);
 		if (err != HTTP_OK)
 			return -1;
@@ -1068,7 +1068,7 @@ static int post_rpc(struct rpc_state *rpc, int stateless_connect, int flush_rece
 	rpc->any_written = 0;
 	err = run_slot(slot, NULL);
 	if (err == HTTP_REAUTH && !large_request) {
-		credential_fill(the_repository, &http_auth, 0);
+		http_reauth_prepare(0);
 		curl_slist_free_all(headers);
 		goto retry;
 	}
@@ -1557,7 +1557,7 @@ int cmd_main(int argc, const char **argv)
 	int nongit;
 	int ret = 1;
 
-	setup_git_directory_gently(&nongit);
+	setup_git_directory_gently(the_repository, &nongit);
 	if (argc < 2) {
 		error(_("remote-curl: usage: git remote-curl <remote> [<url>]"));
 		goto cleanup;
@@ -1605,7 +1605,7 @@ int cmd_main(int argc, const char **argv)
 			break;
 		if (starts_with(buf.buf, "fetch ")) {
 			if (nongit) {
-				setup_git_directory_gently(&nongit);
+				setup_git_directory_gently(the_repository, &nongit);
 				if (nongit)
 					die(_("remote-curl: fetch attempted without a local repo"));
 			}
diff --git a/remote.c b/remote.c
index 7ca2a65..f1a3681 100644
--- a/remote.c
+++ b/remote.c
@@ -8,6 +8,7 @@
 #include "gettext.h"
 #include "hex.h"
 #include "remote.h"
+#include "url.h"
 #include "urlmatch.h"
 #include "refs.h"
 #include "refspec.h"
@@ -152,6 +153,8 @@ static struct remote *make_remote(struct remote_state *remote_state,
 	refspec_init_push(&ret->push);
 	refspec_init_fetch(&ret->fetch);
 	string_list_init_dup(&ret->server_options);
+	string_list_init_dup(&ret->negotiation_restrict);
+	string_list_init_dup(&ret->negotiation_include);
 
 	ALLOC_GROW(remote_state->remotes, remote_state->remotes_nr + 1,
 		   remote_state->remotes_alloc);
@@ -179,6 +182,8 @@ static void remote_clear(struct remote *remote)
 	FREE_AND_NULL(remote->http_proxy);
 	FREE_AND_NULL(remote->http_proxy_authmethod);
 	string_list_clear(&remote->server_options, 0);
+	string_list_clear(&remote->negotiation_restrict, 0);
+	string_list_clear(&remote->negotiation_include, 0);
 }
 
 static void add_merge(struct branch *branch, const char *name)
@@ -562,6 +567,12 @@ static int handle_config(const char *key, const char *value,
 	} else if (!strcmp(subkey, "serveroption")) {
 		return parse_transport_option(key, value,
 					      &remote->server_options);
+	} else if (!strcmp(subkey, "negotiationrestrict")) {
+		return parse_transport_option(key, value,
+					      &remote->negotiation_restrict);
+	} else if (!strcmp(subkey, "negotiationinclude")) {
+		return parse_transport_option(key, value,
+					      &remote->negotiation_include);
 	} else if (!strcmp(subkey, "followremotehead")) {
 		const char *no_warn_branch;
 		if (!strcmp(value, "never"))
@@ -1723,7 +1734,7 @@ void set_ref_status_for_push(struct ref *remote_refs, int send_mirror,
 		if (!reject_reason && !ref->deletion && !is_null_oid(&ref->old_oid)) {
 			if (starts_with(ref->name, "refs/tags/"))
 				reject_reason = REF_STATUS_REJECT_ALREADY_EXISTS;
-			else if (!odb_has_object(the_repository->objects, &ref->old_oid, HAS_OBJECT_RECHECK_PACKED))
+			else if (!odb_has_object(the_repository->objects, &ref->old_oid, ODB_HAS_OBJECT_RECHECK_PACKED))
 				reject_reason = REF_STATUS_REJECT_FETCH_FIRST;
 			else if (!lookup_commit_reference_gently(the_repository, &ref->old_oid, 1) ||
 				 !lookup_commit_reference_gently(the_repository, &ref->new_oid, 1))
diff --git a/remote.h b/remote.h
index fc05294..d8809b6 100644
--- a/remote.h
+++ b/remote.h
@@ -117,6 +117,8 @@ struct remote {
 	char *http_proxy_authmethod;
 
 	struct string_list server_options;
+	struct string_list negotiation_restrict;
+	struct string_list negotiation_include;
 
 	enum follow_remote_head_settings follow_remote_head;
 	const char *no_warn_branch;
diff --git a/repack-geometry.c b/repack-geometry.c
index 7cebd0c..2064683 100644
--- a/repack-geometry.c
+++ b/repack-geometry.c
@@ -4,6 +4,7 @@
 #include "repack.h"
 #include "repository.h"
 #include "hex.h"
+#include "midx.h"
 #include "packfile.h"
 
 static uint32_t pack_geometry_weight(struct packed_git *p)
@@ -31,8 +32,28 @@ void pack_geometry_init(struct pack_geometry *geometry,
 {
 	struct packed_git *p;
 	struct strbuf buf = STRBUF_INIT;
+	struct multi_pack_index *m = get_multi_pack_index(existing->source);
 
 	repo_for_each_pack(existing->repo, p) {
+		if (geometry->midx_layer_threshold_set && m &&
+		    p->multi_pack_index) {
+			/*
+			 * When writing MIDX layers incrementally,
+			 * ignore packs unless they are in the most
+			 * recent MIDX layer *and* there are at least
+			 * 'midx_layer_threshold' packs in that layer.
+			 *
+			 * Otherwise 'p' is either in an older layer, or
+			 * the youngest layer does not have enough packs
+			 * to consider its packs as candidates for
+			 * repacking. In either of those cases we want
+			 * to ignore the pack.
+			 */
+			if (m->num_packs < geometry->midx_layer_threshold ||
+			    !midx_layer_contains_pack(m, pack_basename(p)))
+				continue;
+		}
+
 		if (args->local && !p->pack_local)
 			/*
 			 * When asked to only repack local packfiles we skip
@@ -173,6 +194,20 @@ void pack_geometry_split(struct pack_geometry *geometry)
 	geometry->promisor_split = compute_pack_geometry_split(geometry->promisor_pack,
 							       geometry->promisor_pack_nr,
 							       geometry->split_factor);
+	for (uint32_t i = 0; i < geometry->split; i++) {
+		struct packed_git *p = geometry->pack[i];
+		/*
+		 * During incremental MIDX/bitmap repacking, any packs
+		 * included in the rollup are either (a) not MIDX'd, or
+		 * (b) contained in the tip layer iff it has at least
+		 * the threshold number of packs.
+		 *
+		 * In the latter case, we can safely conclude that the
+		 * tip of the MIDX chain will be rewritten.
+		 */
+		if (p->multi_pack_index)
+			geometry->midx_tip_rewritten = true;
+	}
 }
 
 struct packed_git *pack_geometry_preferred_pack(struct pack_geometry *geometry)
@@ -214,7 +249,8 @@ static void remove_redundant_packs(struct packed_git **pack,
 				   uint32_t pack_nr,
 				   struct string_list *names,
 				   struct existing_packs *existing,
-				   const char *packdir)
+				   const char *packdir,
+				   bool wrote_incremental_midx)
 {
 	const struct git_hash_algo *algop = existing->repo->hash_algo;
 	struct strbuf buf = STRBUF_INIT;
@@ -234,7 +270,8 @@ static void remove_redundant_packs(struct packed_git **pack,
 		    (string_list_has_string(&existing->kept_packs, buf.buf)))
 			continue;
 
-		repack_remove_redundant_pack(existing->repo, packdir, buf.buf);
+		repack_remove_redundant_pack(existing->repo, packdir, buf.buf,
+					     wrote_incremental_midx);
 	}
 
 	strbuf_release(&buf);
@@ -243,12 +280,13 @@ static void remove_redundant_packs(struct packed_git **pack,
 void pack_geometry_remove_redundant(struct pack_geometry *geometry,
 				    struct string_list *names,
 				    struct existing_packs *existing,
-				    const char *packdir)
+				    const char *packdir,
+				    bool wrote_incremental_midx)
 {
 	remove_redundant_packs(geometry->pack, geometry->split,
-			       names, existing, packdir);
+			       names, existing, packdir, wrote_incremental_midx);
 	remove_redundant_packs(geometry->promisor_pack, geometry->promisor_split,
-			       names, existing, packdir);
+			       names, existing, packdir, wrote_incremental_midx);
 }
 
 void pack_geometry_release(struct pack_geometry *geometry)
diff --git a/repack-midx.c b/repack-midx.c
index 0682b80..b6b1de7 100644
--- a/repack-midx.c
+++ b/repack-midx.c
@@ -2,12 +2,16 @@
 #include "repack.h"
 #include "hash.h"
 #include "hex.h"
+#include "lockfile.h"
+#include "midx.h"
 #include "odb.h"
 #include "oidset.h"
 #include "pack-bitmap.h"
+#include "path.h"
 #include "refs.h"
 #include "run-command.h"
 #include "tempfile.h"
+#include "trace2.h"
 
 struct midx_snapshot_ref_data {
 	struct repository *repo;
@@ -275,32 +279,63 @@ static void remove_redundant_bitmaps(struct string_list *include,
 	strbuf_release(&path);
 }
 
-int write_midx_included_packs(struct repack_write_midx_opts *opts)
+static void repack_prepare_midx_command(struct child_process *cmd,
+					struct repack_write_midx_opts *opts,
+					const char *subcommand)
+{
+	cmd->git_cmd = 1;
+
+	strvec_pushl(&cmd->args, "multi-pack-index", subcommand, NULL);
+
+	if (opts->show_progress)
+		strvec_push(&cmd->args, "--progress");
+	else
+		strvec_push(&cmd->args, "--no-progress");
+
+	if (opts->write_bitmaps)
+		strvec_push(&cmd->args, "--bitmap");
+}
+
+static int repack_fill_midx_stdin_packs(struct child_process *cmd,
+					struct string_list *include,
+					struct string_list *out)
+{
+	struct strbuf in_buf = STRBUF_INIT;
+	struct strbuf out_buf = STRBUF_INIT;
+	struct string_list_item *item;
+	int ret;
+
+	strvec_push(&cmd->args, "--stdin-packs");
+
+	for_each_string_list_item(item, include)
+		strbuf_addf(&in_buf, "%s\n", item->string);
+
+	ret = pipe_command(cmd, in_buf.buf, in_buf.len,
+			   out ? &out_buf : NULL, 0, NULL, 0);
+
+	if (out)
+		string_list_split_f(out, out_buf.buf, "\n", -1,
+				    STRING_LIST_SPLIT_NONEMPTY);
+
+	strbuf_release(&in_buf);
+	strbuf_release(&out_buf);
+
+	return ret;
+}
+
+static int write_midx_included_packs(struct repack_write_midx_opts *opts)
 {
 	struct child_process cmd = CHILD_PROCESS_INIT;
 	struct string_list include = STRING_LIST_INIT_DUP;
 	struct string_list_item *item;
 	struct packed_git *preferred = pack_geometry_preferred_pack(opts->geometry);
-	FILE *in;
 	int ret = 0;
 
 	midx_included_packs(&include, opts);
 	if (!include.nr)
 		goto done;
 
-	cmd.in = -1;
-	cmd.git_cmd = 1;
-
-	strvec_push(&cmd.args, "multi-pack-index");
-	strvec_pushl(&cmd.args, "write", "--stdin-packs", NULL);
-
-	if (opts->show_progress)
-		strvec_push(&cmd.args, "--progress");
-	else
-		strvec_push(&cmd.args, "--no-progress");
-
-	if (opts->write_bitmaps)
-		strvec_push(&cmd.args, "--bitmap");
+	repack_prepare_midx_command(&cmd, opts, "write");
 
 	if (preferred)
 		strvec_pushf(&cmd.args, "--preferred-pack=%s",
@@ -342,16 +377,7 @@ int write_midx_included_packs(struct repack_write_midx_opts *opts)
 		strvec_pushf(&cmd.args, "--refs-snapshot=%s",
 			     opts->refs_snapshot);
 
-	ret = start_command(&cmd);
-	if (ret)
-		goto done;
-
-	in = xfdopen(cmd.in, "w");
-	for_each_string_list_item(item, &include)
-		fprintf(in, "%s\n", item->string);
-	fclose(in);
-
-	ret = finish_command(&cmd);
+	ret = repack_fill_midx_stdin_packs(&cmd, &include, NULL);
 done:
 	if (!ret && opts->write_bitmaps)
 		remove_redundant_bitmaps(&include, opts->packdir);
@@ -360,3 +386,637 @@ int write_midx_included_packs(struct repack_write_midx_opts *opts)
 
 	return ret;
 }
+
+struct midx_compaction_step {
+	union {
+		struct multi_pack_index *copy;
+		struct string_list write;
+		struct {
+			struct multi_pack_index *from;
+			struct multi_pack_index *to;
+		} compact;
+	} u;
+
+	uint32_t objects_nr;
+	char *csum;
+
+	enum {
+		MIDX_COMPACTION_STEP_UNKNOWN,
+		MIDX_COMPACTION_STEP_COPY,
+		MIDX_COMPACTION_STEP_WRITE,
+		MIDX_COMPACTION_STEP_COMPACT,
+	} type;
+};
+
+static const char *midx_compaction_step_base(const struct midx_compaction_step *step)
+{
+	switch (step->type) {
+	case MIDX_COMPACTION_STEP_UNKNOWN:
+		BUG("cannot use UNKNOWN step as a base");
+	case MIDX_COMPACTION_STEP_COPY:
+		return midx_get_checksum_hex(step->u.copy);
+	case MIDX_COMPACTION_STEP_WRITE:
+		BUG("cannot use WRITE step as a base");
+	case MIDX_COMPACTION_STEP_COMPACT:
+		return midx_get_checksum_hex(step->u.compact.to);
+	default:
+		BUG("unhandled midx compaction step type %d", step->type);
+	}
+}
+
+static int midx_compaction_step_exec_copy(struct midx_compaction_step *step)
+{
+	step->csum = xstrdup(midx_get_checksum_hex(step->u.copy));
+	return 0;
+}
+
+static int midx_compaction_step_exec_write(struct midx_compaction_step *step,
+					   struct repack_write_midx_opts *opts,
+					   const char *base)
+{
+	struct child_process cmd = CHILD_PROCESS_INIT;
+	struct string_list hash = STRING_LIST_INIT_DUP;
+	struct string_list_item *item;
+	const char *preferred_pack = NULL;
+	int ret = 0;
+
+	if (!step->u.write.nr) {
+		ret = error(_("no packs to write MIDX during compaction"));
+		goto out;
+	}
+
+	for_each_string_list_item(item, &step->u.write) {
+		if (item->util)
+			preferred_pack = item->string;
+	}
+
+	repack_prepare_midx_command(&cmd, opts, "write");
+	strvec_pushl(&cmd.args, "--incremental", "--no-write-chain-file", NULL);
+	strvec_pushf(&cmd.args, "--base=%s", base ? base : "none");
+
+	if (preferred_pack) {
+		struct strbuf buf = STRBUF_INIT;
+
+		strbuf_addstr(&buf, preferred_pack);
+		strbuf_strip_suffix(&buf, ".idx");
+		strbuf_addstr(&buf, ".pack");
+
+		strvec_pushf(&cmd.args, "--preferred-pack=%s", buf.buf);
+
+		strbuf_release(&buf);
+	}
+
+	ret = repack_fill_midx_stdin_packs(&cmd, &step->u.write, &hash);
+	if (hash.nr != 1) {
+		ret = error(_("expected exactly one line during MIDX write, "
+			      "got: %"PRIuMAX),
+			    (uintmax_t)hash.nr);
+		goto out;
+	}
+
+	step->csum = xstrdup(hash.items[0].string);
+
+out:
+	string_list_clear(&hash, 0);
+
+	return ret;
+}
+
+static int midx_compaction_step_exec_compact(struct midx_compaction_step *step,
+					     struct repack_write_midx_opts *opts)
+{
+	struct child_process cmd = CHILD_PROCESS_INIT;
+	struct strbuf buf = STRBUF_INIT;
+	FILE *out = NULL;
+	int ret;
+
+	repack_prepare_midx_command(&cmd, opts, "compact");
+	strvec_pushl(&cmd.args, "--incremental", "--no-write-chain-file",
+		     midx_get_checksum_hex(step->u.compact.from),
+		     midx_get_checksum_hex(step->u.compact.to), NULL);
+
+	cmd.out = -1;
+
+	ret = start_command(&cmd);
+	if (ret)
+		goto out;
+
+	out = xfdopen(cmd.out, "r");
+	while (strbuf_getline_lf(&buf, out) != EOF) {
+		if (step->csum) {
+			ret = error(_("unexpected MIDX output: '%s'"), buf.buf);
+			fclose(out);
+			out = NULL;
+			finish_command(&cmd);
+			goto out;
+		}
+		step->csum = strbuf_detach(&buf, NULL);
+	}
+
+	ret = finish_command(&cmd);
+
+out:
+	if (out)
+		fclose(out);
+	strbuf_release(&buf);
+
+	return ret;
+}
+
+static int midx_compaction_step_exec(struct midx_compaction_step *step,
+				     struct repack_write_midx_opts *opts,
+				     const char *base)
+{
+	switch (step->type) {
+	case MIDX_COMPACTION_STEP_UNKNOWN:
+		BUG("cannot execute UNKNOWN midx compaction step");
+	case MIDX_COMPACTION_STEP_COPY:
+		return midx_compaction_step_exec_copy(step);
+	case MIDX_COMPACTION_STEP_WRITE:
+		return midx_compaction_step_exec_write(step, opts, base);
+	case MIDX_COMPACTION_STEP_COMPACT:
+		return midx_compaction_step_exec_compact(step, opts);
+	default:
+		BUG("unhandled midx compaction step type %d", step->type);
+	}
+}
+
+static void midx_compaction_step_release(struct midx_compaction_step *step)
+{
+	if (step->type == MIDX_COMPACTION_STEP_WRITE)
+		string_list_clear(&step->u.write, 0);
+	free(step->csum);
+}
+
+/*
+ * Build an append-only MIDX plan: a single WRITE step for the freshly
+ * written packs, plus COPY steps for every existing layer.  No
+ * compaction or merging is performed.
+ */
+static void repack_make_midx_append_plan(struct repack_write_midx_opts *opts,
+					 struct midx_compaction_step **steps_p,
+					 size_t *steps_nr_p)
+{
+	struct multi_pack_index *m;
+	struct midx_compaction_step *steps = NULL;
+	struct midx_compaction_step *step;
+	size_t steps_nr = 0, steps_alloc = 0;
+
+	odb_reprepare(opts->existing->repo->objects);
+	m = get_multi_pack_index(opts->existing->source);
+
+	if (opts->names->nr) {
+		struct strbuf buf = STRBUF_INIT;
+		uint32_t i;
+
+		ALLOC_GROW(steps, st_add(steps_nr, 1), steps_alloc);
+
+		step = &steps[steps_nr++];
+		memset(step, 0, sizeof(*step));
+
+		step->type = MIDX_COMPACTION_STEP_WRITE;
+		string_list_init_dup(&step->u.write);
+
+		for (i = 0; i < opts->names->nr; i++) {
+			strbuf_reset(&buf);
+			strbuf_addf(&buf, "pack-%s.idx",
+				    opts->names->items[i].string);
+			string_list_append(&step->u.write, buf.buf);
+		}
+
+		strbuf_release(&buf);
+	}
+
+	for (; m; m = m->base_midx) {
+		ALLOC_GROW(steps, st_add(steps_nr, 1), steps_alloc);
+
+		step = &steps[steps_nr++];
+		memset(step, 0, sizeof(*step));
+
+		step->type = MIDX_COMPACTION_STEP_COPY;
+		step->u.copy = m;
+		step->objects_nr = m->num_objects;
+	}
+
+	*steps_p = steps;
+	*steps_nr_p = steps_nr;
+}
+
+static int repack_make_midx_compaction_plan(struct repack_write_midx_opts *opts,
+					    struct midx_compaction_step **steps_p,
+					    size_t *steps_nr_p)
+{
+	struct multi_pack_index *m;
+	struct midx_compaction_step *steps = NULL;
+	struct midx_compaction_step step = { 0 };
+	struct strbuf buf = STRBUF_INIT;
+	size_t steps_nr = 0, steps_alloc = 0;
+	uint32_t i;
+	int ret = 0;
+
+	trace2_region_enter("repack", "make_midx_compaction_plan",
+			    opts->existing->repo);
+
+	odb_reprepare(opts->existing->repo->objects);
+	m = get_multi_pack_index(opts->existing->source);
+
+	for (i = 0; m && i < m->num_packs + m->num_packs_in_base; i++) {
+		if (prepare_midx_pack(m, i)) {
+			ret = error(_("could not load pack %"PRIu32" from MIDX"),
+				    i);
+			goto out;
+		}
+	}
+
+	trace2_region_enter("repack", "steps:write", opts->existing->repo);
+
+	/*
+	 * The first MIDX in the resulting chain is always going to be
+	 * new.
+	 *
+	 * At a minimum, it will include all of the newly written packs.
+	 * If there is an existing MIDX whose tip layer contains packs
+	 * that were repacked, it will also include any of its packs
+	 * which were *not* rolled up as part of the geometric repack
+	 * (if any), and the previous tip will be replaced.
+	 *
+	 * It may grow to include the packs from zero or more MIDXs from
+	 * the old chain, beginning either at the old tip (if the MIDX
+	 * was *not* rewritten) or the old tip's base MIDX layer
+	 * (otherwise).
+	 */
+	step.type = MIDX_COMPACTION_STEP_WRITE;
+	string_list_init_dup(&step.u.write);
+
+	for (i = 0; i < opts->names->nr; i++) {
+		strbuf_reset(&buf);
+		strbuf_addf(&buf, "pack-%s.idx", opts->names->items[i].string);
+		string_list_append(&step.u.write, buf.buf);
+
+		trace2_data_string("repack", opts->existing->repo,
+				   "include:fresh",
+				   step.u.write.items[step.u.write.nr - 1].string);
+	}
+	for (i = 0; i < opts->geometry->split; i++) {
+		struct packed_git *p = opts->geometry->pack[i];
+		if (unsigned_add_overflows(step.objects_nr, p->num_objects)) {
+			ret = error(_("too many objects in MIDX compaction step"));
+			goto out;
+		}
+
+		step.objects_nr += p->num_objects;
+	}
+	trace2_data_intmax("repack", opts->existing->repo,
+			   "include:fresh:objects_nr",
+			   (uintmax_t)step.objects_nr);
+
+	/*
+	 * Now handle any existing packs which were *not* rewritten.
+	 *
+	 * The list of packs in opts->geometry only contains MIDX'd
+	 * packs from the newest layer when that layer has more than
+	 * 'repack.midxNewLayerThreshold' number of packs.
+	 *
+	 * If the MIDX tip was rewritten (that is, one or more of those
+	 * packs appear below the split line), then add all packs above
+	 * the split line to the new layer, as the old one is no longer
+	 * usable.
+	 *
+	 * If the MIDX tip was not rewritten (that is, all MIDX'd packs
+	 * from the youngest layer appear below the split line, or were
+	 * not included in the geometric repack at all because there
+	 * were too few of them), ignore them since we'll retain the
+	 * existing layer as-is.
+	 */
+	for (i = opts->geometry->split; i < opts->geometry->pack_nr; i++) {
+		struct packed_git *p = opts->geometry->pack[i];
+		struct string_list_item *item;
+
+		strbuf_reset(&buf);
+		strbuf_addstr(&buf, pack_basename(p));
+		strbuf_strip_suffix(&buf, ".pack");
+		strbuf_addstr(&buf, ".idx");
+
+		if (p->multi_pack_index &&
+		    !opts->geometry->midx_tip_rewritten) {
+			trace2_data_string("repack", opts->existing->repo,
+					   "exclude:unmodified", buf.buf);
+			continue;
+		}
+
+		trace2_data_string("repack", opts->existing->repo,
+				   "include:unmodified", buf.buf);
+		trace2_data_string("repack", opts->existing->repo,
+				   "include:unmodified:midx",
+				   p->multi_pack_index ? "true" : "false");
+
+		item = string_list_append(&step.u.write, buf.buf);
+		if (p->multi_pack_index || i == opts->geometry->pack_nr - 1)
+			item->util = (void *)1; /* mark as preferred */
+
+		if (unsigned_add_overflows(step.objects_nr, p->num_objects)) {
+			ret = error(_("too many objects in MIDX compaction step"));
+			goto out;
+		}
+
+		step.objects_nr += p->num_objects;
+	}
+	trace2_data_intmax("repack", opts->existing->repo,
+			   "include:unmodified:objects_nr",
+			   (uintmax_t)step.objects_nr);
+
+	/*
+	 * If the MIDX tip was rewritten, then we no longer consider it
+	 * a candidate for compaction, since it will not exist in the
+	 * MIDX chain being built.
+	 */
+	if (opts->geometry->midx_tip_rewritten)
+		m = m->base_midx;
+
+	trace2_data_string("repack", opts->existing->repo, "midx:rewrote-tip",
+			   opts->geometry->midx_tip_rewritten ? "true" : "false");
+
+	trace2_region_enter("repack", "compact", opts->existing->repo);
+
+	/*
+	 * Compact additional MIDX layers into this proposed one until
+	 * the merging condition is violated.
+	 */
+	while (m) {
+		uint32_t preferred_pack_idx;
+
+		trace2_data_string("repack", opts->existing->repo,
+				   "candidate", midx_get_checksum_hex(m));
+
+		if (step.objects_nr < m->num_objects / opts->midx_split_factor) {
+			/*
+			 * Stop compacting MIDX layer as soon as the
+			 * merged size is less than half the size of the
+			 * next layer in the chain.
+			 */
+			trace2_data_string("repack", opts->existing->repo,
+					   "compact", "violated");
+			trace2_data_intmax("repack", opts->existing->repo,
+					   "objects_nr",
+					   (uintmax_t)step.objects_nr);
+			trace2_data_intmax("repack", opts->existing->repo,
+					   "next_objects_nr",
+					   (uintmax_t)m->num_objects);
+			trace2_data_intmax("repack", opts->existing->repo,
+					   "split_factor",
+					   (uintmax_t)opts->midx_split_factor);
+
+			break;
+		}
+
+		if (midx_preferred_pack(m, &preferred_pack_idx) < 0) {
+			ret = error(_("could not find preferred pack for MIDX "
+				      "%s"), midx_get_checksum_hex(m));
+			goto out;
+		}
+
+		for (i = 0; i < m->num_packs; i++) {
+			struct string_list_item *item;
+			uint32_t pack_int_id = i + m->num_packs_in_base;
+			struct packed_git *p = nth_midxed_pack(m, pack_int_id);
+
+			strbuf_reset(&buf);
+			strbuf_addstr(&buf, pack_basename(p));
+			strbuf_strip_suffix(&buf, ".pack");
+			strbuf_addstr(&buf, ".idx");
+
+			trace2_data_string("repack", opts->existing->repo,
+					   "midx:pack", buf.buf);
+
+			item = string_list_append(&step.u.write, buf.buf);
+			if (pack_int_id == preferred_pack_idx)
+				item->util = (void *)1; /* mark as preferred */
+		}
+
+		if (unsigned_add_overflows(step.objects_nr, m->num_objects)) {
+			ret = error(_("too many objects in MIDX compaction step"));
+			goto out;
+		}
+		step.objects_nr += m->num_objects;
+
+		m = m->base_midx;
+	}
+
+	if (step.u.write.nr > 0) {
+		/*
+		 * As long as there is at least one new pack to write
+		 * (and thus the MIDX is non-empty), add it to the plan.
+		 */
+		ALLOC_GROW(steps, steps_nr + 1, steps_alloc);
+		steps[steps_nr++] = step;
+	}
+
+	trace2_data_intmax("repack", opts->existing->repo,
+			   "step:objects_nr", (uintmax_t)step.objects_nr);
+	trace2_data_intmax("repack", opts->existing->repo,
+			   "step:packs_nr", (uintmax_t)step.u.write.nr);
+
+	trace2_region_leave("repack", "compact", opts->existing->repo);
+	trace2_region_leave("repack", "steps:write", opts->existing->repo);
+
+	trace2_region_enter("repack", "steps:rest", opts->existing->repo);
+
+	/*
+	 * Then start over, repeat, and either compact or keep as-is
+	 * each MIDX layer until we have exhausted the chain.
+	 *
+	 * Finally, evaluate the remainder of the chain (if any) and
+	 * either compact a sequence of adjacent layers, or keep
+	 * individual layers as-is according to the same merging
+	 * condition as above.
+	 */
+	while (m) {
+		struct multi_pack_index *next = m;
+
+		ALLOC_GROW(steps, steps_nr + 1, steps_alloc);
+
+		memset(&step, 0, sizeof(step));
+		step.type = MIDX_COMPACTION_STEP_UNKNOWN;
+
+		trace2_region_enter("repack", "step", opts->existing->repo);
+
+		trace2_data_string("repack", opts->existing->repo,
+				   "from", midx_get_checksum_hex(m));
+
+		while (next) {
+			uint32_t proposed_objects_nr;
+			if (unsigned_add_overflows(step.objects_nr, next->num_objects)) {
+				ret = error(_("too many objects in MIDX compaction step"));
+				trace2_region_leave("repack", "step", opts->existing->repo);
+				goto out;
+			}
+
+			proposed_objects_nr = step.objects_nr + next->num_objects;
+
+			trace2_data_string("repack", opts->existing->repo,
+					   "proposed",
+					   midx_get_checksum_hex(next));
+			trace2_data_intmax("repack", opts->existing->repo,
+					   "proposed:objects_nr",
+					   (uintmax_t)next->num_objects);
+
+			if (!next->base_midx) {
+				/*
+				 * If we are at the end of the MIDX
+				 * chain, there is nothing to compact,
+				 * so mark it and stop.
+				 */
+				step.objects_nr = proposed_objects_nr;
+				break;
+			}
+
+			if (proposed_objects_nr < next->base_midx->num_objects / opts->midx_split_factor) {
+				/*
+				 * If there is a MIDX following this
+				 * one, but our accumulated size is less
+				 * than half of its size, compacting
+				 * them would violate the merging
+				 * condition, so stop here.
+				 */
+
+				trace2_data_string("repack", opts->existing->repo,
+						   "compact:violated:at",
+						   midx_get_checksum_hex(next->base_midx));
+				trace2_data_intmax("repack", opts->existing->repo,
+						   "compact:violated:at:objects_nr",
+						   (uintmax_t)next->base_midx->num_objects);
+				break;
+			}
+
+			/*
+			 * Otherwise, it is OK to compact the next layer
+			 * into this one. Do so, and then continue
+			 * through the remainder of the chain.
+			 */
+			step.objects_nr = proposed_objects_nr;
+			trace2_data_intmax("repack", opts->existing->repo,
+					   "step:objects_nr",
+					   (uintmax_t)step.objects_nr);
+			next = next->base_midx;
+		}
+
+		if (m == next) {
+			step.type = MIDX_COMPACTION_STEP_COPY;
+			step.u.copy = m;
+
+			trace2_data_string("repack", opts->existing->repo,
+					   "type", "copy");
+		} else {
+			step.type = MIDX_COMPACTION_STEP_COMPACT;
+			step.u.compact.from = next;
+			step.u.compact.to = m;
+
+			trace2_data_string("repack", opts->existing->repo,
+					   "to", midx_get_checksum_hex(m));
+			trace2_data_string("repack", opts->existing->repo,
+					   "type", "compact");
+		}
+
+		m = next->base_midx;
+		steps[steps_nr++] = step;
+		trace2_region_leave("repack", "step", opts->existing->repo);
+	}
+
+	trace2_region_leave("repack", "steps:rest", opts->existing->repo);
+
+out:
+	*steps_p = steps;
+	*steps_nr_p = steps_nr;
+
+	strbuf_release(&buf);
+
+	trace2_region_leave("repack", "make_midx_compaction_plan",
+			    opts->existing->repo);
+
+	return ret;
+}
+
+static int write_midx_incremental(struct repack_write_midx_opts *opts)
+{
+	struct midx_compaction_step *steps = NULL;
+	struct strbuf lock_name = STRBUF_INIT;
+	struct lock_file lf;
+	struct strvec keep_hashes = STRVEC_INIT;
+	size_t steps_nr = 0;
+	size_t i;
+	int ret = 0;
+
+	get_midx_chain_filename(opts->existing->source, &lock_name);
+	if (safe_create_leading_directories(opts->existing->repo,
+					    lock_name.buf))
+		die_errno(_("unable to create leading directories of %s"),
+			  lock_name.buf);
+	hold_lock_file_for_update(&lf, lock_name.buf, LOCK_DIE_ON_ERROR);
+
+	if (!fdopen_lock_file(&lf, "w")) {
+		ret = error_errno(_("unable to open multi-pack-index chain file"));
+		goto done;
+	}
+
+	if (opts->geometry->split_factor) {
+		if (repack_make_midx_compaction_plan(opts, &steps, &steps_nr) < 0) {
+			ret = error(_("unable to generate compaction plan"));
+			goto done;
+		}
+	} else {
+		repack_make_midx_append_plan(opts, &steps, &steps_nr);
+	}
+
+	for (i = 0; i < steps_nr; i++) {
+		struct midx_compaction_step *step = &steps[i];
+		char *base = NULL;
+
+		if (i + 1 < steps_nr)
+			base = xstrdup(midx_compaction_step_base(&steps[i + 1]));
+
+		if (midx_compaction_step_exec(step, opts, base) < 0) {
+			ret = error(_("unable to execute compaction step %"PRIuMAX),
+				    (uintmax_t)i);
+			free(base);
+			goto done;
+		}
+
+		free(base);
+	}
+
+	i = steps_nr;
+	while (i--) {
+		struct midx_compaction_step *step = &steps[i];
+		if (!step->csum)
+			BUG("missing result for compaction step %"PRIuMAX,
+			    (uintmax_t)i);
+		fprintf(get_lock_file_fp(&lf), "%s\n", step->csum);
+		strvec_push(&keep_hashes, step->csum);
+	}
+
+	commit_lock_file(&lf);
+
+	clear_incremental_midx_files(opts->existing->repo, &keep_hashes);
+
+done:
+	strvec_clear(&keep_hashes);
+	strbuf_release(&lock_name);
+	for (i = 0; i < steps_nr; i++)
+		midx_compaction_step_release(&steps[i]);
+	free(steps);
+	return ret;
+}
+
+int repack_write_midx(struct repack_write_midx_opts *opts)
+{
+	switch (opts->mode) {
+	case REPACK_WRITE_MIDX_NONE:
+		BUG("write_midx mode is NONE?");
+	case REPACK_WRITE_MIDX_DEFAULT:
+		return write_midx_included_packs(opts);
+	case REPACK_WRITE_MIDX_INCREMENTAL:
+		return write_midx_incremental(opts);
+	default:
+		BUG("unhandled write_midx mode: %d", opts->mode);
+	}
+}
diff --git a/repack.c b/repack.c
index 5968410..571dabb 100644
--- a/repack.c
+++ b/repack.c
@@ -55,14 +55,18 @@ void pack_objects_args_release(struct pack_objects_args *args)
 }
 
 void repack_remove_redundant_pack(struct repository *repo, const char *dir_name,
-				  const char *base_name)
+				  const char *base_name,
+				  bool wrote_incremental_midx)
 {
 	struct strbuf buf = STRBUF_INIT;
 	struct odb_source *source = repo->objects->sources;
 	struct multi_pack_index *m = get_multi_pack_index(source);
 	strbuf_addf(&buf, "%s.pack", base_name);
-	if (m && source->local && midx_contains_pack(m, buf.buf))
+	if (m && source->local && midx_contains_pack(m, buf.buf)) {
 		clear_midx_file(repo);
+		if (!wrote_incremental_midx)
+			clear_incremental_midx_files(repo, NULL);
+	}
 	strbuf_insertf(&buf, 0, "%s/", dir_name);
 	unlink_pack_path(buf.buf, 1);
 	strbuf_release(&buf);
@@ -154,6 +158,8 @@ void existing_packs_collect(struct existing_packs *existing,
 			string_list_append(&existing->non_kept_packs, buf.buf);
 	}
 
+	existing->source = existing->repo->objects->sources;
+
 	string_list_sort(&existing->kept_packs);
 	string_list_sort(&existing->non_kept_packs);
 	string_list_sort(&existing->cruft_packs);
@@ -248,25 +254,63 @@ void existing_packs_mark_for_deletion(struct existing_packs *existing,
 					   &existing->cruft_packs);
 }
 
+/*
+ * Mark every pack that is referenced by the existing MIDX chain as
+ * retained, so that a subsequent call to
+ * existing_packs_mark_for_deletion() will not mark them for deletion.
+ *
+ * This is used when writing an incremental MIDX layer on top of an
+ * existing chain: retained layers continue to reference the same
+ * packs on disk, so those packs must not be unlinked even if the
+ * freshly-written pack supersedes them.
+ */
+void existing_packs_retain_midx_packs(struct existing_packs *existing)
+{
+	struct string_list_item *item;
+	struct strbuf buf = STRBUF_INIT;
+
+	for_each_string_list_item(item, &existing->midx_packs) {
+		struct string_list_item *found;
+
+		strbuf_reset(&buf);
+		strbuf_addstr(&buf, item->string);
+		strbuf_strip_suffix(&buf, ".pack");
+		strbuf_strip_suffix(&buf, ".idx");
+
+		found = string_list_lookup(&existing->non_kept_packs, buf.buf);
+		if (found)
+			existing_packs_mark_retained(found);
+
+		found = string_list_lookup(&existing->cruft_packs, buf.buf);
+		if (found)
+			existing_packs_mark_retained(found);
+	}
+
+	strbuf_release(&buf);
+}
+
 static void remove_redundant_packs_1(struct repository *repo,
 				     struct string_list *packs,
-				     const char *packdir)
+				     const char *packdir,
+				     bool wrote_incremental_midx)
 {
 	struct string_list_item *item;
 	for_each_string_list_item(item, packs) {
 		if (!existing_pack_is_marked_for_deletion(item))
 			continue;
-		repack_remove_redundant_pack(repo, packdir, item->string);
+		repack_remove_redundant_pack(repo, packdir, item->string,
+					     wrote_incremental_midx);
 	}
 }
 
 void existing_packs_remove_redundant(struct existing_packs *existing,
-				     const char *packdir)
+				     const char *packdir,
+				     bool wrote_incremental_midx)
 {
 	remove_redundant_packs_1(existing->repo, &existing->non_kept_packs,
-				 packdir);
+				 packdir, wrote_incremental_midx);
 	remove_redundant_packs_1(existing->repo, &existing->cruft_packs,
-				 packdir);
+				 packdir, wrote_incremental_midx);
 }
 
 void existing_packs_release(struct existing_packs *existing)
diff --git a/repack.h b/repack.h
index bc9f2e1..f9fbc89 100644
--- a/repack.h
+++ b/repack.h
@@ -34,7 +34,8 @@ void prepare_pack_objects(struct child_process *cmd,
 void pack_objects_args_release(struct pack_objects_args *args);
 
 void repack_remove_redundant_pack(struct repository *repo, const char *dir_name,
-				  const char *base_name);
+				  const char *base_name,
+				  bool wrote_incremental_midx);
 
 struct write_pack_opts {
 	struct pack_objects_args *po_args;
@@ -56,6 +57,7 @@ struct packed_git;
 
 struct existing_packs {
 	struct repository *repo;
+	struct odb_source *source;
 	struct string_list kept_packs;
 	struct string_list non_kept_packs;
 	struct string_list cruft_packs;
@@ -82,8 +84,10 @@ void existing_packs_retain_cruft(struct existing_packs *existing,
 				 struct packed_git *cruft);
 void existing_packs_mark_for_deletion(struct existing_packs *existing,
 				      struct string_list *names);
+void existing_packs_retain_midx_packs(struct existing_packs *existing);
 void existing_packs_remove_redundant(struct existing_packs *existing,
-				     const char *packdir);
+				     const char *packdir,
+				     bool wrote_incremental_midx);
 void existing_packs_release(struct existing_packs *existing);
 
 struct generated_pack;
@@ -107,6 +111,10 @@ struct pack_geometry {
 	uint32_t promisor_pack_nr, promisor_pack_alloc;
 	uint32_t promisor_split;
 
+	uint32_t midx_layer_threshold;
+	bool midx_layer_threshold_set;
+	bool midx_tip_rewritten;
+
 	int split_factor;
 };
 
@@ -124,11 +132,18 @@ struct packed_git *pack_geometry_preferred_pack(struct pack_geometry *geometry);
 void pack_geometry_remove_redundant(struct pack_geometry *geometry,
 				    struct string_list *names,
 				    struct existing_packs *existing,
-				    const char *packdir);
+				    const char *packdir,
+				    bool wrote_incremental_midx);
 void pack_geometry_release(struct pack_geometry *geometry);
 
 struct tempfile;
 
+enum repack_write_midx_mode {
+	REPACK_WRITE_MIDX_NONE,
+	REPACK_WRITE_MIDX_DEFAULT,
+	REPACK_WRITE_MIDX_INCREMENTAL,
+};
+
 struct repack_write_midx_opts {
 	struct existing_packs *existing;
 	struct pack_geometry *geometry;
@@ -138,10 +153,13 @@ struct repack_write_midx_opts {
 	int show_progress;
 	int write_bitmaps;
 	int midx_must_contain_cruft;
+	int midx_split_factor;
+	int midx_new_layer_threshold;
+	enum repack_write_midx_mode mode;
 };
 
 void midx_snapshot_refs(struct repository *repo, struct tempfile *f);
-int write_midx_included_packs(struct repack_write_midx_opts *opts);
+int repack_write_midx(struct repack_write_midx_opts *opts);
 
 int write_filtered_pack(const struct write_pack_opts *opts,
 			struct existing_packs *existing,
diff --git a/replay.c b/replay.c
index d7239d4..da531d5 100644
--- a/replay.c
+++ b/replay.c
@@ -120,7 +120,7 @@ static struct commit *create_commit(struct repository *repo,
 out:
 	repo_unuse_commit_buffer(repo, based_on, message);
 	free_commit_extra_headers(extra);
-	free_commit_list(parents);
+	commit_list_free(parents);
 	strbuf_release(&msg);
 	free(author);
 	return (struct commit *)obj;
@@ -254,7 +254,10 @@ static struct commit *mapped_commit(kh_oid_map_t *replayed_commits,
 				    struct commit *commit,
 				    struct commit *fallback)
 {
-	khint_t pos = kh_get_oid_map(replayed_commits, commit->object.oid);
+	khint_t pos;
+	if (!commit)
+		return fallback;
+	pos = kh_get_oid_map(replayed_commits, commit->object.oid);
 	if (pos == kh_end(replayed_commits))
 		return fallback;
 	return kh_value(replayed_commits, pos);
@@ -266,23 +269,32 @@ static struct commit *pick_regular_commit(struct repository *repo,
 					  struct commit *onto,
 					  struct merge_options *merge_opt,
 					  struct merge_result *result,
-					  enum replay_mode mode)
+					  enum replay_mode mode,
+					  enum replay_empty_commit_action empty)
 {
 	struct commit *base, *replayed_base;
 	struct tree *pickme_tree, *base_tree, *replayed_base_tree;
 
-	base = pickme->parents->item;
-	replayed_base = mapped_commit(replayed_commits, base, onto);
+	if (pickme->parents) {
+		base = pickme->parents->item;
+		base_tree = repo_get_commit_tree(repo, base);
+	} else {
+		base = NULL;
+		base_tree = lookup_tree(repo, repo->hash_algo->empty_tree);
+	}
 
+	replayed_base = mapped_commit(replayed_commits, base, onto);
 	replayed_base_tree = repo_get_commit_tree(repo, replayed_base);
 	pickme_tree = repo_get_commit_tree(repo, pickme);
-	base_tree = repo_get_commit_tree(repo, base);
 
 	if (mode == REPLAY_MODE_PICK) {
 		/* Cherry-pick: normal order */
 		merge_opt->branch1 = short_commit_name(repo, replayed_base);
 		merge_opt->branch2 = short_commit_name(repo, pickme);
-		merge_opt->ancestor = xstrfmt("parent of %s", merge_opt->branch2);
+		if (pickme->parents)
+			merge_opt->ancestor = xstrfmt("parent of %s", merge_opt->branch2);
+		else
+			merge_opt->ancestor = xstrdup("empty tree");
 
 		merge_incore_nonrecursive(merge_opt,
 					  base_tree,
@@ -310,12 +322,25 @@ static struct commit *pick_regular_commit(struct repository *repo,
 	}
 	merge_opt->ancestor = NULL;
 	merge_opt->branch2 = NULL;
+
 	if (!result->clean)
 		return NULL;
-	/* Drop commits that become empty */
+
+	/* Handle commits that become empty */
 	if (oideq(&replayed_base_tree->object.oid, &result->tree->object.oid) &&
-	    !oideq(&pickme_tree->object.oid, &base_tree->object.oid))
-		return replayed_base;
+	    !oideq(&pickme_tree->object.oid, &base_tree->object.oid)) {
+		switch (empty) {
+		case REPLAY_EMPTY_COMMIT_DROP:
+			return replayed_base;
+		case REPLAY_EMPTY_COMMIT_KEEP:
+			break;
+		case REPLAY_EMPTY_COMMIT_ABORT:
+			result->clean = error(_("commit %s became empty after replay"),
+					      oid_to_hex(&pickme->object.oid));
+			return NULL;
+		}
+	}
+
 	return create_commit(repo, result->tree, pickme, replayed_base, mode);
 }
 
@@ -347,13 +372,15 @@ int replay_revisions(struct rev_info *revs,
 	struct commit *last_commit = NULL;
 	struct commit *commit;
 	struct commit *onto = NULL;
-	struct merge_options merge_opt;
+	struct merge_options merge_opt = { 0 };
 	struct merge_result result = {
 		.clean = 1,
 	};
 	bool detached_head;
 	char *advance;
 	char *revert;
+	const char *ref;
+	struct object_id old_oid;
 	enum replay_mode mode = REPLAY_MODE_PICK;
 	int ret;
 
@@ -364,7 +391,26 @@ int replay_revisions(struct rev_info *revs,
 	set_up_replay_mode(revs->repo, &revs->cmdline, opts->onto,
 			   &detached_head, &advance, &revert, &onto, &update_refs);
 
-	/* FIXME: Should allow replaying commits with the first as a root commit */
+	if (opts->ref) {
+		struct object_id oid;
+
+		if (update_refs && strset_get_size(update_refs) > 1) {
+			ret = error(_("'--ref' cannot be used with multiple revision ranges"));
+			goto out;
+		}
+		if (check_refname_format(opts->ref, 0) || !starts_with(opts->ref, "refs/")) {
+			ret = error(_("'%s' is not a valid refname"), opts->ref);
+			goto out;
+		}
+		ref = opts->ref;
+		if (!refs_read_ref(get_main_ref_store(revs->repo), opts->ref, &oid))
+			oidcpy(&old_oid, &oid);
+		else
+			oidclr(&old_oid, revs->repo->hash_algo);
+	} else {
+		ref = advance ? advance : revert;
+		oidcpy(&old_oid, &onto->object.oid);
+	}
 
 	if (prepare_revision_walk(revs) < 0) {
 		ret = error(_("error preparing revisions"));
@@ -380,14 +426,12 @@ int replay_revisions(struct rev_info *revs,
 		khint_t pos;
 		int hr;
 
-		if (!commit->parents)
-			die(_("replaying down from root commit is not supported yet!"));
-		if (commit->parents->next)
+		if (commit->parents && commit->parents->next)
 			die(_("replaying merge commits is not supported yet!"));
 
 		last_commit = pick_regular_commit(revs->repo, commit, replayed_commits,
 						  mode == REPLAY_MODE_REVERT ? last_commit : onto,
-						  &merge_opt, &result, mode);
+						  &merge_opt, &result, mode, opts->empty);
 		if (!last_commit)
 			break;
 
@@ -399,7 +443,7 @@ int replay_revisions(struct rev_info *revs,
 		kh_value(replayed_commits, pos) = last_commit;
 
 		/* Update any necessary branches */
-		if (advance || revert)
+		if (ref)
 			continue;
 
 		for (decoration = get_name_decoration(&commit->object);
@@ -428,18 +472,19 @@ int replay_revisions(struct rev_info *revs,
 		}
 	}
 
+	if (result.clean < 0) {
+		ret = -1;
+		goto out;
+	}
+
 	if (!result.clean) {
 		ret = 1;
 		goto out;
 	}
 
-	/* In --advance or --revert mode, update the target ref */
-	if (advance || revert) {
-		const char *ref = advance ? advance : revert;
-		replay_result_queue_update(out, ref,
-					   &onto->object.oid,
+	if (ref)
+		replay_result_queue_update(out, ref, &old_oid,
 					   &last_commit->object.oid);
-	}
 
 	ret = 0;
 
diff --git a/replay.h b/replay.h
index e916a5f..1851a07 100644
--- a/replay.h
+++ b/replay.h
@@ -7,6 +7,19 @@ struct repository;
 struct rev_info;
 
 /*
+ * Controls what happens when a replayed commit becomes empty (i.e. its tree
+ * is identical to its parent's tree after the replay).
+ */
+enum replay_empty_commit_action {
+	/* Silently discard the empty commit. */
+	REPLAY_EMPTY_COMMIT_DROP,
+	/* Keep the empty commit as-is. */
+	REPLAY_EMPTY_COMMIT_KEEP,
+	/* Abort with an error. */
+	REPLAY_EMPTY_COMMIT_ABORT,
+};
+
+/*
  * A set of options that can be passed to `replay_revisions()`.
  */
 struct replay_revisions_options {
@@ -25,6 +38,13 @@ struct replay_revisions_options {
 	const char *onto;
 
 	/*
+	 * Reference to update with the result of the replay. This will not
+	 * update any refs from `onto`, `advance`, or `revert`. Ignores
+	 * `contained`.
+	 */
+	const char *ref;
+
+	/*
 	 * Starting point at which to create revert commits; must be a branch
 	 * name. The branch will be updated to point to the revert commits.
 	 * This option is mutually exclusive with `onto` and `advance`.
@@ -36,6 +56,12 @@ struct replay_revisions_options {
 	 * Requires `onto` to be set.
 	 */
 	int contained;
+
+	/*
+	 * Controls what to do when a replayed commit becomes empty.
+	 * Defaults to REPLAY_EMPTY_COMMIT_DROP.
+	 */
+	enum replay_empty_commit_action empty;
 };
 
 /* This struct is used as an out-parameter by `replay_revisions()`. */
diff --git a/repository.c b/repository.c
index 9e5537f..db57b83 100644
--- a/repository.c
+++ b/repository.c
@@ -323,6 +323,7 @@ int repo_init(struct repository *repo,
 	return 0;
 
 error:
+	clear_repository_format(&format);
 	repo_clear(repo);
 	return -1;
 }
@@ -425,6 +426,8 @@ void repo_clear(struct repository *repo)
 		hook_cache_clear(repo->hook_config_cache);
 		FREE_AND_NULL(repo->hook_config_cache);
 	}
+	strmap_clear(&repo->event_jobs, 0); /* values are uintptr_t, not heap ptrs */
+	string_list_clear(&repo->disabled_events, 0);
 
 	if (repo->promisor_remote_config) {
 		promisor_remote_clear(repo->promisor_remote_config);
diff --git a/repository.h b/repository.h
index 078059a..c3ec0f4 100644
--- a/repository.h
+++ b/repository.h
@@ -2,6 +2,7 @@
 #define REPOSITORY_H
 
 #include "strmap.h"
+#include "string-list.h"
 #include "repo-settings.h"
 #include "environment.h"
 
@@ -113,6 +114,8 @@ struct repository {
 	 * A NULL value indicates that there is no working directory.
 	 */
 	char *worktree;
+	bool worktree_initialized;
+	bool worktree_config_is_bogus;
 
 	/*
 	 * Path from the root of the top-level superproject down to this
@@ -172,6 +175,15 @@ struct repository {
 	 */
 	struct strmap *hook_config_cache;
 
+	/* Cached value of hook.jobs config (0 if unset, defaults to serial). */
+	unsigned int hook_jobs;
+
+	/* Cached map of event-name -> jobs count (as uintptr_t) from hook.<event>.jobs. */
+	struct strmap event_jobs;
+
+	/* Cached list of event names with hook.<event>.enabled = false. */
+	struct string_list disabled_events;
+
 	/* Configurations related to promisor remotes. */
 	char *repository_format_partial_clone;
 	struct promisor_remote_config *promisor_remote_config;
@@ -269,6 +281,6 @@ void repo_update_index_if_able(struct repository *, struct lock_file *);
  * Return 1 if upgrade repository format to target_version succeeded,
  * 0 if no upgrade is necessary, and -1 when upgrade is not possible.
  */
-int upgrade_repository_format(int target_version);
+int upgrade_repository_format(struct repository *repo, int target_version);
 
 #endif /* REPOSITORY_H */
diff --git a/revision.c b/revision.c
index 31808e3..5693618 100644
--- a/revision.c
+++ b/revision.c
@@ -473,10 +473,10 @@ static struct commit *handle_commit(struct rev_info *revs,
 	die("%s is unknown object", name);
 }
 
-static int everybody_uninteresting(struct commit_list *orig,
+static int everybody_uninteresting(struct prio_queue *orig,
 				   struct commit **interesting_cache)
 {
-	struct commit_list *list = orig;
+	size_t i;
 
 	if (*interesting_cache) {
 		struct commit *commit = *interesting_cache;
@@ -484,9 +484,8 @@ static int everybody_uninteresting(struct commit_list *orig,
 			return 0;
 	}
 
-	while (list) {
-		struct commit *commit = list->item;
-		list = list->next;
+	for (i = 0; i < orig->nr; i++) {
+		struct commit *commit = orig->array[i].data;
 		if (commit->object.flags & UNINTERESTING)
 			continue;
 
@@ -1300,20 +1299,17 @@ static void cherry_pick_list(struct commit_list *list, struct rev_info *revs)
 /* How many extra uninteresting commits we want to see.. */
 #define SLOP 5
 
-static int still_interesting(struct commit_list *src, timestamp_t date, int slop,
+static int still_interesting(struct prio_queue *src, timestamp_t date, int slop,
 			     struct commit **interesting_cache)
 {
 	/*
-	 * No source list at all? We're definitely done..
+	 * Since src is sorted by date, it is enough to peek at the
+	 * first entry to compare dates.  No entry at all means done.
 	 */
-	if (!src)
+	struct commit *commit = prio_queue_peek(src);
+	if (!commit)
 		return 0;
-
-	/*
-	 * Does the destination list contain entries with a date
-	 * before the source list? Definitely _not_ done.
-	 */
-	if (date <= src->item->date)
+	if (date <= commit->date)
 		return SLOP;
 
 	/*
@@ -1451,6 +1447,7 @@ static int limit_list(struct rev_info *revs)
 	struct commit_list *newlist = NULL;
 	struct commit_list **p = &newlist;
 	struct commit *interesting_cache = NULL;
+	struct prio_queue queue = { .compare = compare_commits_by_commit_date };
 
 	if (revs->ancestry_path_implicit_bottoms) {
 		collect_bottom_commits(original_list,
@@ -1461,6 +1458,11 @@ static int limit_list(struct rev_info *revs)
 
 	while (original_list) {
 		struct commit *commit = pop_commit(&original_list);
+		prio_queue_put(&queue, commit);
+	}
+
+	while (queue.nr) {
+		struct commit *commit = prio_queue_get(&queue);
 		struct object *obj = &commit->object;
 
 		if (commit == interesting_cache)
@@ -1468,11 +1470,13 @@ static int limit_list(struct rev_info *revs)
 
 		if (revs->max_age != -1 && (commit->date < revs->max_age))
 			obj->flags |= UNINTERESTING;
-		if (process_parents(revs, commit, &original_list, NULL) < 0)
+		if (process_parents(revs, commit, NULL, &queue) < 0) {
+			clear_prio_queue(&queue);
 			return -1;
+		}
 		if (obj->flags & UNINTERESTING) {
 			mark_parents_uninteresting(revs, commit);
-			slop = still_interesting(original_list, date, slop, &interesting_cache);
+			slop = still_interesting(&queue, date, slop, &interesting_cache);
 			if (slop)
 				continue;
 			break;
@@ -1509,7 +1513,7 @@ static int limit_list(struct rev_info *revs)
 		}
 	}
 
-	commit_list_free(original_list);
+	clear_prio_queue(&queue);
 	revs->commits = newlist;
 	return 0;
 }
@@ -2038,41 +2042,32 @@ static void prepare_show_merge(struct rev_info *revs)
 	free(prune);
 }
 
-static int dotdot_missing(const char *arg, char *dotdot,
+static int dotdot_missing(const char *full_name,
 			  struct rev_info *revs, int symmetric)
 {
 	if (revs->ignore_missing)
 		return 0;
-	/* de-munge so we report the full argument */
-	*dotdot = '.';
 	die(symmetric
 	    ? "Invalid symmetric difference expression %s"
-	    : "Invalid revision range %s", arg);
+	    : "Invalid revision range %s", full_name);
 }
 
-static int handle_dotdot_1(const char *arg, char *dotdot,
+static int handle_dotdot_1(const char *a_name, const char *b_name,
+			   const char *full_name, int symmetric,
 			   struct rev_info *revs, int flags,
 			   int cant_be_filename,
 			   struct object_context *a_oc,
 			   struct object_context *b_oc)
 {
-	const char *a_name, *b_name;
 	struct object_id a_oid, b_oid;
 	struct object *a_obj, *b_obj;
 	unsigned int a_flags, b_flags;
-	int symmetric = 0;
 	unsigned int flags_exclude = flags ^ (UNINTERESTING | BOTTOM);
 	unsigned int oc_flags = GET_OID_COMMITTISH | GET_OID_RECORD_PATH;
 
-	a_name = arg;
 	if (!*a_name)
 		a_name = "HEAD";
 
-	b_name = dotdot + 2;
-	if (*b_name == '.') {
-		symmetric = 1;
-		b_name++;
-	}
 	if (!*b_name)
 		b_name = "HEAD";
 
@@ -2081,15 +2076,13 @@ static int handle_dotdot_1(const char *arg, char *dotdot,
 		return -1;
 
 	if (!cant_be_filename) {
-		*dotdot = '.';
-		verify_non_filename(revs->prefix, arg);
-		*dotdot = '\0';
+		verify_non_filename(the_repository, revs->prefix, full_name);
 	}
 
 	a_obj = parse_object(revs->repo, &a_oid);
 	b_obj = parse_object(revs->repo, &b_oid);
 	if (!a_obj || !b_obj)
-		return dotdot_missing(arg, dotdot, revs, symmetric);
+		return dotdot_missing(full_name, revs, symmetric);
 
 	if (!symmetric) {
 		/* just A..B */
@@ -2103,7 +2096,7 @@ static int handle_dotdot_1(const char *arg, char *dotdot,
 		a = lookup_commit_reference(revs->repo, &a_obj->oid);
 		b = lookup_commit_reference(revs->repo, &b_obj->oid);
 		if (!a || !b)
-			return dotdot_missing(arg, dotdot, revs, symmetric);
+			return dotdot_missing(full_name, revs, symmetric);
 
 		if (repo_get_merge_bases(the_repository, a, b, &exclude) < 0) {
 			commit_list_free(exclude);
@@ -2132,16 +2125,23 @@ static int handle_dotdot(const char *arg,
 			 int cant_be_filename)
 {
 	struct object_context a_oc = {0}, b_oc = {0};
-	char *dotdot = strstr(arg, "..");
+	const char *dotdot = strstr(arg, "..");
+	char *tmp;
+	int symmetric = 0;
 	int ret;
 
 	if (!dotdot)
 		return -1;
 
-	*dotdot = '\0';
-	ret = handle_dotdot_1(arg, dotdot, revs, flags, cant_be_filename,
-			      &a_oc, &b_oc);
-	*dotdot = '.';
+	tmp = xmemdupz(arg, dotdot - arg);
+	dotdot += 2;
+	if (*dotdot == '.') {
+		symmetric = 1;
+		dotdot++;
+	}
+	ret = handle_dotdot_1(tmp, dotdot, arg, symmetric, revs, flags,
+			      cant_be_filename, &a_oc, &b_oc);
+	free(tmp);
 
 	object_context_release(&a_oc);
 	object_context_release(&b_oc);
@@ -2151,7 +2151,10 @@ static int handle_dotdot(const char *arg,
 static int handle_revision_arg_1(const char *arg_, struct rev_info *revs, int flags, unsigned revarg_opt)
 {
 	struct object_context oc = {0};
-	char *mark;
+	const char *mark;
+	char *arg_minus_at = NULL;
+	char *arg_minus_excl = NULL;
+	char *arg_minus_dash = NULL;
 	struct object *object;
 	struct object_id oid;
 	int local_flags;
@@ -2178,18 +2181,17 @@ static int handle_revision_arg_1(const char *arg_, struct rev_info *revs, int fl
 
 	mark = strstr(arg, "^@");
 	if (mark && !mark[2]) {
-		*mark = 0;
-		if (add_parents_only(revs, arg, flags, 0)) {
+		arg_minus_at = xmemdupz(arg, mark - arg);
+		if (add_parents_only(revs, arg_minus_at, flags, 0)) {
 			ret = 0;
 			goto out;
 		}
-		*mark = '^';
 	}
 	mark = strstr(arg, "^!");
 	if (mark && !mark[2]) {
-		*mark = 0;
-		if (!add_parents_only(revs, arg, flags ^ (UNINTERESTING | BOTTOM), 0))
-			*mark = '^';
+		arg_minus_excl = xmemdupz(arg, mark - arg);
+		if (add_parents_only(revs, arg_minus_excl, flags ^ (UNINTERESTING | BOTTOM), 0))
+			arg = arg_minus_excl;
 	}
 	mark = strstr(arg, "^-");
 	if (mark) {
@@ -2203,9 +2205,9 @@ static int handle_revision_arg_1(const char *arg_, struct rev_info *revs, int fl
 			}
 		}
 
-		*mark = 0;
-		if (!add_parents_only(revs, arg, flags ^ (UNINTERESTING | BOTTOM), exclude_parent))
-			*mark = '^';
+		arg_minus_dash = xmemdupz(arg, mark - arg);
+		if (add_parents_only(revs, arg_minus_dash, flags ^ (UNINTERESTING | BOTTOM), exclude_parent))
+			arg = arg_minus_dash;
 	}
 
 	local_flags = 0;
@@ -2227,7 +2229,7 @@ static int handle_revision_arg_1(const char *arg_, struct rev_info *revs, int fl
 		goto out;
 	}
 	if (!cant_be_filename)
-		verify_non_filename(revs->prefix, arg);
+		verify_non_filename(the_repository, revs->prefix, arg);
 	object = get_reference(revs, arg, &oid, flags ^ local_flags);
 	if (!object) {
 		ret = (revs->ignore_missing || revs->do_not_die_on_missing_objects) ? 0 : -1;
@@ -2240,6 +2242,9 @@ static int handle_revision_arg_1(const char *arg_, struct rev_info *revs, int fl
 
 out:
 	object_context_release(&oc);
+	free(arg_minus_at);
+	free(arg_minus_excl);
+	free(arg_minus_dash);
 	return ret;
 }
 
@@ -2605,6 +2610,8 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
 	} else if (!strcmp(arg, "--no-graph")) {
 		graph_clear(revs->graph);
 		revs->graph = NULL;
+	} else if (skip_prefix(arg, "--graph-lane-limit=", &optarg)) {
+		revs->graph_max_lanes = parse_count(optarg);
 	} else if (!strcmp(arg, "--encode-email-headers")) {
 		revs->encode_email_headers = 1;
 	} else if (!strcmp(arg, "--no-encode-email-headers")) {
@@ -3066,7 +3073,7 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s
 			 * but the latter we have checked in the main loop.
 			 */
 			for (j = i; j < argc; j++)
-				verify_filename(revs->prefix, argv[j], j == i);
+				verify_filename(the_repository, revs->prefix, argv[j], j == i);
 
 			strvec_pushv(&prune_data, argv + i);
 			break;
@@ -3128,6 +3135,8 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s
 		if (want_ancestry(revs))
 			revs->limited = 1;
 		revs->topo_order = 1;
+		if (!revs->diffopt.output_format)
+			revs->diffopt.output_format = DIFF_FORMAT_PATCH;
 	}
 
 	if (revs->topo_order && !generation_numbers_enabled(the_repository))
@@ -3172,6 +3181,10 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s
 
 	if (revs->no_walk && revs->graph)
 		die(_("options '%s' and '%s' cannot be used together"), "--no-walk", "--graph");
+
+	if (revs->graph_max_lanes > 0 && !revs->graph)
+		die(_("the option '%s' requires '%s'"), "--graph-lane-limit", "--graph");
+
 	if (!revs->reflog_info && revs->grep_filter.use_reflog_filter)
 		die(_("the option '%s' requires '%s'"), "--grep-reflog", "--walk-reflogs");
 
diff --git a/revision.h b/revision.h
index 584f133..c9a1182 100644
--- a/revision.h
+++ b/revision.h
@@ -305,6 +305,7 @@ struct rev_info {
 
 	/* Display history graph */
 	struct git_graph *graph;
+	int graph_max_lanes;
 
 	/* special limits */
 	int skip_count;
diff --git a/run-command.c b/run-command.c
index 32c290e..e70a8a3 100644
--- a/run-command.c
+++ b/run-command.c
@@ -545,6 +545,7 @@ static void atfork_parent(struct atfork_state *as)
 		"restoring signal mask");
 #endif
 }
+
 #endif /* GIT_WINDOWS_NATIVE */
 
 static inline void set_cloexec(int fd)
@@ -604,11 +605,11 @@ static void trace_add_env(struct strbuf *dst, const char *const *deltaenv)
 	/* Last one wins, see run-command.c:prep_childenv() for context */
 	for (e = deltaenv; e && *e; e++) {
 		struct strbuf key = STRBUF_INIT;
-		char *equals = strchr(*e, '=');
+		const char *equals = strchr(*e, '=');
 
 		if (equals) {
 			strbuf_add(&key, *e, equals - *e);
-			string_list_insert(&envs, key.buf)->util = equals + 1;
+			string_list_insert(&envs, key.buf)->util = (void *)(equals + 1);
 		} else {
 			string_list_insert(&envs, *e)->util = NULL;
 		}
@@ -831,6 +832,17 @@ int start_command(struct child_process *cmd)
 			child_close(cmd->out);
 		}
 
+		if (cmd->close_fd_above_stderr) {
+			long max_fd = sysconf(_SC_OPEN_MAX);
+			int fd;
+			if (max_fd < 0 || max_fd > 4096)
+				max_fd = 4096;
+			for (fd = 3; fd < max_fd; fd++) {
+				if (fd != child_notifier)
+					close(fd);
+			}
+		}
+
 		if (cmd->dir && chdir(cmd->dir))
 			child_die(CHILD_ERR_CHDIR);
 
@@ -1895,14 +1907,19 @@ void run_processes_parallel(const struct run_process_parallel_opts *opts)
 					   "max:%"PRIuMAX,
 					   (uintmax_t)opts->processes);
 
+	pp_init(&pp, opts, &pp_sig);
+
 	/*
 	 * Child tasks might receive input via stdin, terminating early (or not), so
 	 * ignore the default SIGPIPE which gets handled by each feed_pipe_fn which
 	 * actually writes the data to children stdin fds.
+	 *
+	 * This _must_ come after pp_init(), because it installs its own
+	 * SIGPIPE handler (to cleanup children), and we want to supersede
+	 * that.
 	 */
 	sigchain_push(SIGPIPE, SIG_IGN);
 
-	pp_init(&pp, opts, &pp_sig);
 	while (1) {
 		for (i = 0;
 		    i < spawn_cap && !pp.shutdown &&
@@ -1928,10 +1945,10 @@ void run_processes_parallel(const struct run_process_parallel_opts *opts)
 		}
 	}
 
-	pp_cleanup(&pp, opts);
-
 	sigchain_pop(SIGPIPE);
 
+	pp_cleanup(&pp, opts);
+
 	if (do_trace2)
 		trace2_region_leave(tr2_category, tr2_label, NULL);
 }
@@ -1939,10 +1956,14 @@ void run_processes_parallel(const struct run_process_parallel_opts *opts)
 int prepare_auto_maintenance(struct repository *r, int quiet,
 			     struct child_process *maint)
 {
-	int enabled, auto_detach;
+	int enabled = 1, auto_detach;
 
-	if (!repo_config_get_bool(r, "maintenance.auto", &enabled) &&
-	    !enabled)
+	if (repo_config_get_bool(r, "maintenance.auto", &enabled)) {
+		int gc_threshold;
+		if (!repo_config_get_int(r, "gc.auto", &gc_threshold))
+			enabled = gc_threshold > 0;
+	}
+	if (!enabled)
 		return 0;
 
 	/*
diff --git a/run-command.h b/run-command.h
index 8ca496d..c2fad4f 100644
--- a/run-command.h
+++ b/run-command.h
@@ -143,6 +143,15 @@ struct child_process {
 	unsigned stdout_to_stderr:1;
 	unsigned clean_on_exit:1;
 	unsigned wait_after_clean:1;
+
+	/**
+	 * Close file descriptors 3 and above in the child after forking
+	 * but before exec.  This prevents the child from inheriting
+	 * pipe endpoints or other descriptors from the parent
+	 * environment (e.g., the test harness).
+	 */
+	unsigned close_fd_above_stderr:1;
+
 	void (*clean_on_exit_handler)(struct child_process *process);
 };
 
diff --git a/scalar.c b/scalar.c
index 4efb6ac..a80d8ee 100644
--- a/scalar.c
+++ b/scalar.c
@@ -58,7 +58,7 @@ static void setup_enlistment_directory(int argc, const char **argv,
 	}
 	strbuf_setlen(&path, len);
 
-	setup_git_directory();
+	setup_git_directory(the_repository);
 
 	if (!the_repository->worktree)
 		die(_("Scalar enlistments require a worktree"));
@@ -514,7 +514,7 @@ static int cmd_clone(int argc, const char **argv)
 		goto cleanup;
 	}
 
-	setup_git_directory();
+	setup_git_directory(the_repository);
 
 	/* common-main already logs `argv` */
 	trace2_def_repo(the_repository);
diff --git a/send-pack.c b/send-pack.c
index 07ecfae..3bb5afc 100644
--- a/send-pack.c
+++ b/send-pack.c
@@ -175,8 +175,8 @@ static int receive_status(struct repository *r,
 	ret = receive_unpack_status(reader);
 	while (1) {
 		struct object_id old_oid, new_oid;
-		const char *head;
-		const char *refname;
+		char *head;
+		char *refname;
 		char *p;
 		if (packet_reader_read(reader) != PACKET_READ_NORMAL)
 			break;
@@ -190,7 +190,8 @@ static int receive_status(struct repository *r,
 		*p++ = '\0';
 
 		if (!strcmp(head, "option")) {
-			const char *key, *val;
+			char *key;
+			const char *val;
 
 			if (!hint || !(report || new_report)) {
 				if (!once++)
@@ -433,28 +434,48 @@ static void reject_invalid_nonce(const char *nonce, int len)
 
 static void get_commons_through_negotiation(struct repository *r,
 					    const char *url,
+					    const struct string_list *negotiation_include,
+					    const struct string_list *negotiation_restrict,
 					    const struct ref *remote_refs,
 					    struct oid_array *commons)
 {
 	struct child_process child = CHILD_PROCESS_INIT;
 	const struct ref *ref;
 	int len = r->hash_algo->hexsz + 1; /* hash + NL */
-	int nr_negotiation_tip = 0;
+	int nr_negotiation = 0;
 
 	child.git_cmd = 1;
 	child.no_stdin = 1;
 	child.out = -1;
 	strvec_pushl(&child.args, "fetch", "--negotiate-only", NULL);
-	for (ref = remote_refs; ref; ref = ref->next) {
-		if (!is_null_oid(&ref->new_oid)) {
-			strvec_pushf(&child.args, "--negotiation-tip=%s",
-				     oid_to_hex(&ref->new_oid));
-			nr_negotiation_tip++;
+
+	if (negotiation_restrict && negotiation_restrict->nr) {
+		struct string_list_item *item;
+		for_each_string_list_item(item, negotiation_restrict)
+			strvec_pushf(&child.args, "--negotiation-restrict=%s",
+				     item->string);
+		nr_negotiation = negotiation_restrict->nr;
+	} else {
+		for (ref = remote_refs; ref; ref = ref->next) {
+			if (!is_null_oid(&ref->new_oid)) {
+				strvec_pushf(&child.args, "--negotiation-restrict=%s",
+					     oid_to_hex(&ref->new_oid));
+				nr_negotiation++;
+			}
 		}
 	}
+
+	if (negotiation_include && negotiation_include->nr) {
+		struct string_list_item *item;
+		for_each_string_list_item(item, negotiation_include)
+			strvec_pushf(&child.args, "--negotiation-include=%s",
+				     item->string);
+		nr_negotiation += negotiation_include->nr;
+	}
+
 	strvec_push(&child.args, url);
 
-	if (!nr_negotiation_tip) {
+	if (!nr_negotiation) {
 		child_process_clear(&child);
 		return;
 	}
@@ -528,7 +549,10 @@ int send_pack(struct repository *r,
 	repo_config_get_bool(r, "push.negotiate", &push_negotiate);
 	if (push_negotiate) {
 		trace2_region_enter("send_pack", "push_negotiate", r);
-		get_commons_through_negotiation(r, args->url, remote_refs, &commons);
+		get_commons_through_negotiation(r, args->url,
+					       args->negotiation_include,
+					       args->negotiation_restrict,
+					       remote_refs, &commons);
 		trace2_region_leave("send_pack", "push_negotiate", r);
 	}
 
diff --git a/send-pack.h b/send-pack.h
index c5ded2d..13850c9 100644
--- a/send-pack.h
+++ b/send-pack.h
@@ -18,6 +18,8 @@ struct repository;
 
 struct send_pack_args {
 	const char *url;
+	const struct string_list *negotiation_include;
+	const struct string_list *negotiation_restrict;
 	unsigned verbose:1,
 		quiet:1,
 		porcelain:1,
diff --git a/sequencer.c b/sequencer.c
index b7d8dca..1ee4b28 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -4657,7 +4657,9 @@ static enum todo_command peek_command(struct todo_list *todo_list, int offset)
 
 static void create_autostash_internal(struct repository *r,
 				      const char *path,
-				      const char *refname)
+				      const char *refname,
+				      const char *message,
+				      bool silent)
 {
 	struct strbuf buf = STRBUF_INIT;
 	struct lock_file lock_file = LOCK_INIT;
@@ -4679,7 +4681,8 @@ static void create_autostash_internal(struct repository *r,
 		struct object_id oid;
 
 		strvec_pushl(&stash.args,
-			     "stash", "create", "autostash", NULL);
+			     "stash", "create",
+			     message ? message : "autostash", NULL);
 		stash.git_cmd = 1;
 		stash.no_stdin = 1;
 		strbuf_reset(&buf);
@@ -4702,7 +4705,8 @@ static void create_autostash_internal(struct repository *r,
 					&oid, null_oid(the_hash_algo), 0, UPDATE_REFS_DIE_ON_ERR);
 		}
 
-		printf(_("Created autostash: %s\n"), buf.buf);
+		if (!silent)
+			printf(_("Created autostash: %s\n"), buf.buf);
 		if (reset_head(r, &ropts) < 0)
 			die(_("could not reset --hard"));
 		discard_index(r->index);
@@ -4714,15 +4718,19 @@ static void create_autostash_internal(struct repository *r,
 
 void create_autostash(struct repository *r, const char *path)
 {
-	create_autostash_internal(r, path, NULL);
+	create_autostash_internal(r, path, NULL, NULL, false);
 }
 
-void create_autostash_ref(struct repository *r, const char *refname)
+void create_autostash_ref(struct repository *r, const char *refname,
+			  const char *message, bool silent)
 {
-	create_autostash_internal(r, NULL, refname);
+	create_autostash_internal(r, NULL, refname, message, silent);
 }
 
-static int apply_save_autostash_oid(const char *stash_oid, int attempt_apply)
+static int apply_save_autostash_oid(const char *stash_oid, int attempt_apply,
+				    const char *label_ours, const char *label_theirs,
+				    const char *label_base,
+				    const char *stash_msg)
 {
 	struct child_process child = CHILD_PROCESS_INIT;
 	int ret = 0;
@@ -4733,6 +4741,12 @@ static int apply_save_autostash_oid(const char *stash_oid, int attempt_apply)
 		child.no_stderr = 1;
 		strvec_push(&child.args, "stash");
 		strvec_push(&child.args, "apply");
+		if (label_ours)
+			strvec_pushf(&child.args, "--label-ours=%s", label_ours);
+		if (label_theirs)
+			strvec_pushf(&child.args, "--label-theirs=%s", label_theirs);
+		if (label_base)
+			strvec_pushf(&child.args, "--label-base=%s", label_base);
 		strvec_push(&child.args, stash_oid);
 		ret = run_command(&child);
 	}
@@ -4746,20 +4760,24 @@ static int apply_save_autostash_oid(const char *stash_oid, int attempt_apply)
 		strvec_push(&store.args, "stash");
 		strvec_push(&store.args, "store");
 		strvec_push(&store.args, "-m");
-		strvec_push(&store.args, "autostash");
+		strvec_push(&store.args, stash_msg ? stash_msg : "autostash");
 		strvec_push(&store.args, "-q");
 		strvec_push(&store.args, stash_oid);
 		if (run_command(&store))
 			ret = error(_("cannot store %s"), stash_oid);
+		else if (attempt_apply)
+			fprintf(stderr,
+				_("Your local changes are stashed, however applying them\n"
+				  "resulted in conflicts.  You can either resolve the conflicts\n"
+				  "and then discard the stash with \"git stash drop\", or, if you\n"
+				  "do not want to resolve them now, run \"git reset --hard\" and\n"
+				  "apply the local changes later by running \"git stash pop\".\n"));
 		else
 			fprintf(stderr,
-				_("%s\n"
+				_("Autostash exists; creating a new stash entry.\n"
 				  "Your changes are safe in the stash.\n"
 				  "You can run \"git stash pop\" or"
-				  " \"git stash drop\" at any time.\n"),
-				attempt_apply ?
-				_("Applying autostash resulted in conflicts.") :
-				_("Autostash exists; creating a new stash entry."));
+				  " \"git stash drop\" at any time.\n"));
 	}
 
 	return ret;
@@ -4777,7 +4795,8 @@ static int apply_save_autostash(const char *path, int attempt_apply)
 	}
 	strbuf_trim(&stash_oid);
 
-	ret = apply_save_autostash_oid(stash_oid.buf, attempt_apply);
+	ret = apply_save_autostash_oid(stash_oid.buf, attempt_apply,
+				      NULL, NULL, NULL, NULL);
 
 	unlink(path);
 	strbuf_release(&stash_oid);
@@ -4796,11 +4815,14 @@ int apply_autostash(const char *path)
 
 int apply_autostash_oid(const char *stash_oid)
 {
-	return apply_save_autostash_oid(stash_oid, 1);
+	return apply_save_autostash_oid(stash_oid, 1, NULL, NULL, NULL, NULL);
 }
 
 static int apply_save_autostash_ref(struct repository *r, const char *refname,
-				    int attempt_apply)
+				    int attempt_apply,
+				    const char *label_ours, const char *label_theirs,
+				    const char *label_base,
+				    const char *stash_msg)
 {
 	struct object_id stash_oid;
 	char stash_oid_hex[GIT_MAX_HEXSZ + 1];
@@ -4816,7 +4838,9 @@ static int apply_save_autostash_ref(struct repository *r, const char *refname,
 		return error(_("autostash reference is a symref"));
 
 	oid_to_hex_r(stash_oid_hex, &stash_oid);
-	ret = apply_save_autostash_oid(stash_oid_hex, attempt_apply);
+	ret = apply_save_autostash_oid(stash_oid_hex, attempt_apply,
+				       label_ours, label_theirs, label_base,
+				       stash_msg);
 
 	refs_delete_ref(get_main_ref_store(r), "", refname,
 			&stash_oid, REF_NO_DEREF);
@@ -4826,12 +4850,17 @@ static int apply_save_autostash_ref(struct repository *r, const char *refname,
 
 int save_autostash_ref(struct repository *r, const char *refname)
 {
-	return apply_save_autostash_ref(r, refname, 0);
+	return apply_save_autostash_ref(r, refname, 0,
+					NULL, NULL, NULL, NULL);
 }
 
-int apply_autostash_ref(struct repository *r, const char *refname)
+int apply_autostash_ref(struct repository *r, const char *refname,
+			const char *label_ours, const char *label_theirs,
+			const char *label_base, const char *stash_msg)
 {
-	return apply_save_autostash_ref(r, refname, 1);
+	return apply_save_autostash_ref(r, refname, 1,
+					label_ours, label_theirs, label_base,
+					stash_msg);
 }
 
 static int checkout_onto(struct repository *r, struct replay_opts *opts,
@@ -6409,7 +6438,6 @@ struct todo_add_branch_context {
 	size_t items_nr;
 	size_t items_alloc;
 	struct strbuf *buf;
-	struct commit *commit;
 	struct string_list refs_to_oids;
 };
 
@@ -6431,8 +6459,14 @@ static int add_decorations_to_list(const struct commit *commit,
 		/*
 		 * If the branch is the current HEAD, then it will be
 		 * updated by the default rebase behavior.
+		 * Exclude it from the list of refs to update,
+		 * as well as any non-branch decorations.
+		 * Non-branch decorations may be present if the pretty format
+		 * includes "%d", which would have loaded all refs
+		 * into the global decoration table.
 		 */
-		if (head_ref && !strcmp(head_ref, decoration->name)) {
+		if ((head_ref && !strcmp(head_ref, decoration->name)) ||
+		    (decoration->type != DECORATION_REF_LOCAL)) {
 			decoration = decoration->next;
 			continue;
 		}
@@ -6498,7 +6532,6 @@ static int todo_list_add_update_ref_commands(struct todo_list *todo_list)
 		ctx.items[ctx.items_nr++] = todo_list->items[i++];
 
 		if (item->commit) {
-			ctx.commit = item->commit;
 			add_decorations_to_list(item->commit, &ctx);
 		}
 	}
diff --git a/sequencer.h b/sequencer.h
index a6fa670..3164bd4 100644
--- a/sequencer.h
+++ b/sequencer.h
@@ -229,12 +229,15 @@ void commit_post_rewrite(struct repository *r,
 			 const struct object_id *new_head);
 
 void create_autostash(struct repository *r, const char *path);
-void create_autostash_ref(struct repository *r, const char *refname);
+void create_autostash_ref(struct repository *r, const char *refname,
+			  const char *message, bool silent);
 int save_autostash(const char *path);
 int save_autostash_ref(struct repository *r, const char *refname);
 int apply_autostash(const char *path);
 int apply_autostash_oid(const char *stash_oid);
-int apply_autostash_ref(struct repository *r, const char *refname);
+int apply_autostash_ref(struct repository *r, const char *refname,
+			const char *label_ours, const char *label_theirs,
+			const char *label_base, const char *stash_msg);
 
 #define SUMMARY_INITIAL_COMMIT   (1 << 0)
 #define SUMMARY_SHOW_AUTHOR_DATE (1 << 1)
diff --git a/setup.c b/setup.c
index 7ec4427..075bf89 100644
--- a/setup.c
+++ b/setup.c
@@ -26,9 +26,6 @@
 #include "trace2.h"
 #include "worktree.h"
 
-static int inside_git_dir = -1;
-static int inside_work_tree = -1;
-static int work_tree_config_is_bogus;
 enum allowed_bare_repo {
 	ALLOWED_BARE_REPO_EXPLICIT = 0,
 	ALLOWED_BARE_REPO_ALL,
@@ -50,13 +47,13 @@ const char *tmp_original_cwd;
  * /dir/repolink/file     (repolink points to /dir/repo) -> file
  * /dir/repo              (exactly equal to work tree)   -> (empty string)
  */
-static int abspath_part_inside_repo(char *path)
+static int abspath_part_inside_repo(struct repository *repo, char *path)
 {
 	size_t len;
 	size_t wtlen;
 	char *path0;
 	int off;
-	const char *work_tree = precompose_string_if_needed(repo_get_work_tree(the_repository));
+	const char *work_tree = precompose_string_if_needed(repo_get_work_tree(repo));
 	struct strbuf realpath = STRBUF_INIT;
 
 	if (!work_tree)
@@ -119,7 +116,8 @@ static int abspath_part_inside_repo(char *path)
  *  ../../sub1/sub2/foo -> sub1/sub2/foo (but no remaining prefix)
  *  `pwd`/../bar -> sub1/bar       (no remaining prefix)
  */
-char *prefix_path_gently(const char *prefix, int len,
+char *prefix_path_gently(struct repository *repo,
+			 const char *prefix, int len,
 			 int *remaining_prefix, const char *path)
 {
 	const char *orig = path;
@@ -132,7 +130,7 @@ char *prefix_path_gently(const char *prefix, int len,
 			free(sanitized);
 			return NULL;
 		}
-		if (abspath_part_inside_repo(sanitized)) {
+		if (abspath_part_inside_repo(repo, sanitized)) {
 			free(sanitized);
 			return NULL;
 		}
@@ -148,23 +146,23 @@ char *prefix_path_gently(const char *prefix, int len,
 	return sanitized;
 }
 
-char *prefix_path(const char *prefix, int len, const char *path)
+char *prefix_path(struct repository *repo, const char *prefix, int len, const char *path)
 {
-	char *r = prefix_path_gently(prefix, len, NULL, path);
+	char *r = prefix_path_gently(repo, prefix, len, NULL, path);
 	if (!r) {
-		const char *hint_path = repo_get_work_tree(the_repository);
+		const char *hint_path = repo_get_work_tree(repo);
 		if (!hint_path)
-			hint_path = repo_get_git_dir(the_repository);
+			hint_path = repo_get_git_dir(repo);
 		die(_("'%s' is outside repository at '%s'"), path,
 		    absolute_path(hint_path));
 	}
 	return r;
 }
 
-int path_inside_repo(const char *prefix, const char *path)
+int path_inside_repo(struct repository *repo, const char *prefix, const char *path)
 {
 	int len = prefix ? strlen(prefix) : 0;
-	char *r = prefix_path_gently(prefix, len, NULL, path);
+	char *r = prefix_path_gently(repo, prefix, len, NULL, path);
 	if (r) {
 		free(r);
 		return 1;
@@ -281,7 +279,8 @@ static int looks_like_pathspec(const char *arg)
  * diagnose_misspelt_rev == 0 for the next ones (because we already
  * saw a filename, there's not ambiguity anymore).
  */
-void verify_filename(const char *prefix,
+void verify_filename(struct repository *repo,
+		     const char *prefix,
 		     const char *arg,
 		     int diagnose_misspelt_rev)
 {
@@ -289,7 +288,7 @@ void verify_filename(const char *prefix,
 		die(_("option '%s' must come before non-option arguments"), arg);
 	if (looks_like_pathspec(arg) || check_filename(prefix, arg))
 		return;
-	die_verify_filename(the_repository, prefix, arg, diagnose_misspelt_rev);
+	die_verify_filename(repo, prefix, arg, diagnose_misspelt_rev);
 }
 
 /*
@@ -297,9 +296,9 @@ void verify_filename(const char *prefix,
  * and we parsed the arg as a refname.  It should not be interpretable
  * as a filename.
  */
-void verify_non_filename(const char *prefix, const char *arg)
+void verify_non_filename(struct repository *repo, const char *prefix, const char *arg)
 {
-	if (!is_inside_work_tree() || is_inside_git_dir())
+	if (!is_inside_work_tree(repo) || is_inside_git_dir(repo))
 		return;
 	if (*arg == '-')
 		return; /* flag */
@@ -470,32 +469,38 @@ int is_nonbare_repository_dir(struct strbuf *path)
 	return ret;
 }
 
-int is_inside_git_dir(void)
+int is_inside_git_dir(struct repository *repo)
 {
-	if (inside_git_dir < 0)
-		inside_git_dir = is_inside_dir(repo_get_git_dir(the_repository));
-	return inside_git_dir;
+	struct strbuf buf = STRBUF_INIT;
+	int ret = is_inside_dir(strbuf_realpath(&buf, repo_get_git_dir(repo), 1));
+	strbuf_release(&buf);
+	return ret;
 }
 
-int is_inside_work_tree(void)
+int is_inside_work_tree(struct repository *repo)
 {
-	if (inside_work_tree < 0)
-		inside_work_tree = is_inside_dir(repo_get_work_tree(the_repository));
-	return inside_work_tree;
+	struct strbuf buf = STRBUF_INIT;
+	const char *worktree;
+	int ret;
+
+	worktree = repo_get_work_tree(repo);
+	if (!worktree)
+		return 0;
+
+	ret = is_inside_dir(strbuf_realpath(&buf, worktree, 1));
+
+	strbuf_release(&buf);
+	return ret;
 }
 
-void setup_work_tree(void)
+void setup_work_tree(struct repository *repo)
 {
 	const char *work_tree;
-	static int initialized = 0;
 
-	if (initialized)
-		return;
-
-	if (work_tree_config_is_bogus)
+	if (repo->worktree_config_is_bogus)
 		die(_("unable to set up work tree using invalid config"));
 
-	work_tree = repo_get_work_tree(the_repository);
+	work_tree = repo_get_work_tree(repo);
 	if (!work_tree || chdir_notify(work_tree))
 		die(_("this operation must be run in a work tree"));
 
@@ -505,11 +510,9 @@ void setup_work_tree(void)
 	 */
 	if (getenv(GIT_WORK_TREE_ENVIRONMENT))
 		setenv(GIT_WORK_TREE_ENVIRONMENT, ".", 1);
-
-	initialized = 1;
 }
 
-static void setup_original_cwd(void)
+static void setup_original_cwd(struct repository *repo)
 {
 	struct strbuf tmp = STRBUF_INIT;
 	const char *worktree = NULL;
@@ -535,9 +538,9 @@ static void setup_original_cwd(void)
 
 	/* Normalize the directory */
 	if (!strbuf_realpath(&tmp, tmp_original_cwd, 0)) {
-		trace2_data_string("setup", the_repository,
+		trace2_data_string("setup", repo,
 				   "realpath-path", tmp_original_cwd);
-		trace2_data_string("setup", the_repository,
+		trace2_data_string("setup", repo,
 				   "realpath-failure", strerror(errno));
 		free((char*)tmp_original_cwd);
 		tmp_original_cwd = NULL;
@@ -552,7 +555,7 @@ static void setup_original_cwd(void)
 	 * Get our worktree; we only protect the current working directory
 	 * if it's in the worktree.
 	 */
-	worktree = repo_get_work_tree(the_repository);
+	worktree = repo_get_work_tree(repo);
 	if (!worktree)
 		goto no_prevention_needed;
 
@@ -747,7 +750,10 @@ static int check_repo_format(const char *var, const char *value,
 	return read_worktree_config(var, value, ctx, vdata);
 }
 
-static int check_repository_format_gently(const char *gitdir, struct repository_format *candidate, int *nongit_ok)
+static int check_repository_format_gently(struct repository *repo,
+					  const char *gitdir,
+					  struct repository_format *candidate,
+					  int *nongit_ok)
 {
 	struct strbuf sb = STRBUF_INIT;
 	struct strbuf err = STRBUF_INIT;
@@ -776,7 +782,7 @@ static int check_repository_format_gently(const char *gitdir, struct repository_
 		die("%s", err.buf);
 	}
 
-	the_repository->repository_format_precious_objects = candidate->precious_objects;
+	repo->repository_format_precious_objects = candidate->precious_objects;
 
 	string_list_clear(&candidate->unknown_extensions, 0);
 	string_list_clear(&candidate->v1_only_extensions, 0);
@@ -795,20 +801,17 @@ static int check_repository_format_gently(const char *gitdir, struct repository_
 	if (!has_common) {
 		if (candidate->is_bare != -1) {
 			is_bare_repository_cfg = candidate->is_bare;
-			if (is_bare_repository_cfg == 1)
-				inside_work_tree = -1;
 		}
 		if (candidate->work_tree) {
 			free(git_work_tree_cfg);
 			git_work_tree_cfg = xstrdup(candidate->work_tree);
-			inside_work_tree = -1;
 		}
 	}
 
 	return 0;
 }
 
-int upgrade_repository_format(int target_version)
+int upgrade_repository_format(struct repository *repo, int target_version)
 {
 	struct strbuf sb = STRBUF_INIT;
 	struct strbuf err = STRBUF_INIT;
@@ -816,7 +819,7 @@ int upgrade_repository_format(int target_version)
 	struct repository_format repo_fmt = REPOSITORY_FORMAT_INIT;
 	int ret;
 
-	repo_common_path_append(the_repository, &sb, "config");
+	repo_common_path_append(repo, &sb, "config");
 	read_repository_format(&repo_fmt, sb.buf);
 	strbuf_release(&sb);
 
@@ -838,7 +841,7 @@ int upgrade_repository_format(int target_version)
 	}
 
 	strbuf_addf(&repo_version, "%d", target_version);
-	repo_config_set(the_repository, "core.repositoryformatversion", repo_version.buf);
+	repo_config_set(repo, "core.repositoryformatversion", repo_version.buf);
 
 	ret = 1;
 
@@ -1034,7 +1037,8 @@ const char *read_gitfile_gently(const char *path, int *return_error_code)
 	return error_code ? NULL : path;
 }
 
-static void setup_git_env_internal(const char *git_dir,
+static void setup_git_env_internal(struct repository *repo,
+				   const char *git_dir,
 				   bool skip_initializing_odb)
 {
 	char *git_replace_ref_base;
@@ -1052,7 +1056,7 @@ static void setup_git_env_internal(const char *git_dir,
 		args.disable_ref_updates = true;
 	args.skip_initializing_odb = skip_initializing_odb;
 
-	repo_set_gitdir(the_repository, git_dir, &args);
+	repo_set_gitdir(repo, git_dir, &args);
 	strvec_clear(&to_free);
 
 	if (getenv(NO_REPLACE_OBJECTS_ENVIRONMENT))
@@ -1064,38 +1068,39 @@ static void setup_git_env_internal(const char *git_dir,
 
 	shallow_file = getenv(GIT_SHALLOW_FILE_ENVIRONMENT);
 	if (shallow_file)
-		set_alternate_shallow_file(the_repository, shallow_file, 0);
+		set_alternate_shallow_file(repo, shallow_file, 0);
 
 	if (git_env_bool(NO_LAZY_FETCH_ENVIRONMENT, 0))
 		fetch_if_missing = 0;
 }
 
-void setup_git_env(const char *git_dir)
+static void setup_git_env(struct repository *repo, const char *git_dir)
 {
-	setup_git_env_internal(git_dir, false);
+	setup_git_env_internal(repo, git_dir, false);
 }
 
-static void set_git_dir_1(const char *path, bool skip_initializing_odb)
+static void set_git_dir_1(struct repository *repo, const char *path, bool skip_initializing_odb)
 {
 	xsetenv(GIT_DIR_ENVIRONMENT, path, 1);
-	setup_git_env_internal(path, skip_initializing_odb);
+	setup_git_env_internal(repo, path, skip_initializing_odb);
 }
 
 static void update_relative_gitdir(const char *name UNUSED,
 				   const char *old_cwd,
 				   const char *new_cwd,
-				   void *data UNUSED)
+				   void *data)
 {
+	struct repository *repo = data;
 	char *path = reparent_relative_path(old_cwd, new_cwd,
-					    repo_get_git_dir(the_repository));
+					    repo_get_git_dir(repo));
 	trace_printf_key(&trace_setup_key,
 			 "setup: move $GIT_DIR to '%s'",
 			 path);
-	set_git_dir_1(path, true);
+	set_git_dir_1(repo, path, true);
 	free(path);
 }
 
-static void set_git_dir(const char *path, int make_realpath)
+static void set_git_dir(struct repository *repo, const char *path, int make_realpath)
 {
 	struct strbuf realpath = STRBUF_INIT;
 
@@ -1104,14 +1109,15 @@ static void set_git_dir(const char *path, int make_realpath)
 		path = realpath.buf;
 	}
 
-	set_git_dir_1(path, false);
+	set_git_dir_1(repo, path, false);
 	if (!is_absolute_path(path))
-		chdir_notify_register(NULL, update_relative_gitdir, NULL);
+		chdir_notify_register(NULL, update_relative_gitdir, repo);
 
 	strbuf_release(&realpath);
 }
 
-static const char *setup_explicit_git_dir(const char *gitdirenv,
+static const char *setup_explicit_git_dir(struct repository *repo,
+					  const char *gitdirenv,
 					  struct strbuf *cwd,
 					  struct repository_format *repo_fmt,
 					  int *nongit_ok)
@@ -1139,29 +1145,29 @@ static const char *setup_explicit_git_dir(const char *gitdirenv,
 		die(_("not a git repository: '%s'"), gitdirenv);
 	}
 
-	if (check_repository_format_gently(gitdirenv, repo_fmt, nongit_ok)) {
+	if (check_repository_format_gently(repo, gitdirenv, repo_fmt, nongit_ok)) {
 		free(gitfile);
 		return NULL;
 	}
 
 	/* #3, #7, #11, #15, #19, #23, #27, #31 (see t1510) */
 	if (work_tree_env)
-		set_git_work_tree(work_tree_env);
+		set_git_work_tree(repo, work_tree_env);
 	else if (is_bare_repository_cfg > 0) {
 		if (git_work_tree_cfg) {
 			/* #22.2, #30 */
 			warning("core.bare and core.worktree do not make sense");
-			work_tree_config_is_bogus = 1;
+			repo->worktree_config_is_bogus = true;
 		}
 
 		/* #18, #26 */
-		set_git_dir(gitdirenv, 0);
+		set_git_dir(repo, gitdirenv, 0);
 		free(gitfile);
 		return NULL;
 	}
 	else if (git_work_tree_cfg) { /* #6, #14 */
 		if (is_absolute_path(git_work_tree_cfg))
-			set_git_work_tree(git_work_tree_cfg);
+			set_git_work_tree(repo, git_work_tree_cfg);
 		else {
 			char *core_worktree;
 			if (chdir(gitdirenv))
@@ -1171,32 +1177,32 @@ static const char *setup_explicit_git_dir(const char *gitdirenv,
 			core_worktree = xgetcwd();
 			if (chdir(cwd->buf))
 				die_errno(_("cannot come back to cwd"));
-			set_git_work_tree(core_worktree);
+			set_git_work_tree(repo, core_worktree);
 			free(core_worktree);
 		}
 	}
 	else if (!git_env_bool(GIT_IMPLICIT_WORK_TREE_ENVIRONMENT, 1)) {
 		/* #16d */
-		set_git_dir(gitdirenv, 0);
+		set_git_dir(repo, gitdirenv, 0);
 		free(gitfile);
 		return NULL;
 	}
 	else /* #2, #10 */
-		set_git_work_tree(".");
+		set_git_work_tree(repo, ".");
 
 	/* set_git_work_tree() must have been called by now */
-	worktree = repo_get_work_tree(the_repository);
+	worktree = repo_get_work_tree(repo);
 
 	/* both repo_get_work_tree() and cwd are already normalized */
 	if (!strcmp(cwd->buf, worktree)) { /* cwd == worktree */
-		set_git_dir(gitdirenv, 0);
+		set_git_dir(repo, gitdirenv, 0);
 		free(gitfile);
 		return NULL;
 	}
 
 	offset = dir_inside_of(cwd->buf, worktree);
 	if (offset >= 0) {	/* cwd inside worktree? */
-		set_git_dir(gitdirenv, 1);
+		set_git_dir(repo, gitdirenv, 1);
 		if (chdir(worktree))
 			die_errno(_("cannot chdir to '%s'"), worktree);
 		strbuf_addch(cwd, '/');
@@ -1205,17 +1211,18 @@ static const char *setup_explicit_git_dir(const char *gitdirenv,
 	}
 
 	/* cwd outside worktree */
-	set_git_dir(gitdirenv, 0);
+	set_git_dir(repo, gitdirenv, 0);
 	free(gitfile);
 	return NULL;
 }
 
-static const char *setup_discovered_git_dir(const char *gitdir,
+static const char *setup_discovered_git_dir(struct repository *repo,
+					    const char *gitdir,
 					    struct strbuf *cwd, int offset,
 					    struct repository_format *repo_fmt,
 					    int *nongit_ok)
 {
-	if (check_repository_format_gently(gitdir, repo_fmt, nongit_ok))
+	if (check_repository_format_gently(repo, gitdir, repo_fmt, nongit_ok))
 		return NULL;
 
 	/* --work-tree is set without --git-dir; use discovered one */
@@ -1227,25 +1234,23 @@ static const char *setup_discovered_git_dir(const char *gitdir,
 			gitdir = to_free = real_pathdup(gitdir, 1);
 		if (chdir(cwd->buf))
 			die_errno(_("cannot come back to cwd"));
-		ret = setup_explicit_git_dir(gitdir, cwd, repo_fmt, nongit_ok);
+		ret = setup_explicit_git_dir(repo, gitdir, cwd, repo_fmt, nongit_ok);
 		free(to_free);
 		return ret;
 	}
 
 	/* #16.2, #17.2, #20.2, #21.2, #24, #25, #28, #29 (see t1510) */
 	if (is_bare_repository_cfg > 0) {
-		set_git_dir(gitdir, (offset != cwd->len));
+		set_git_dir(repo, gitdir, (offset != cwd->len));
 		if (chdir(cwd->buf))
 			die_errno(_("cannot come back to cwd"));
 		return NULL;
 	}
 
 	/* #0, #1, #5, #8, #9, #12, #13 */
-	set_git_work_tree(".");
+	set_git_work_tree(repo, ".");
 	if (strcmp(gitdir, DEFAULT_GIT_DIR_ENVIRONMENT))
-		set_git_dir(gitdir, 0);
-	inside_git_dir = 0;
-	inside_work_tree = 1;
+		set_git_dir(repo, gitdir, 0);
 	if (offset >= cwd->len)
 		return NULL;
 
@@ -1258,13 +1263,14 @@ static const char *setup_discovered_git_dir(const char *gitdir,
 }
 
 /* #16.1, #17.1, #20.1, #21.1, #22.1 (see t1510) */
-static const char *setup_bare_git_dir(struct strbuf *cwd, int offset,
+static const char *setup_bare_git_dir(struct repository *repo,
+				      struct strbuf *cwd, int offset,
 				      struct repository_format *repo_fmt,
 				      int *nongit_ok)
 {
 	int root_len;
 
-	if (check_repository_format_gently(".", repo_fmt, nongit_ok))
+	if (check_repository_format_gently(repo, ".", repo_fmt, nongit_ok))
 		return NULL;
 
 	setenv(GIT_IMPLICIT_WORK_TREE_ENVIRONMENT, "0", 1);
@@ -1276,20 +1282,18 @@ static const char *setup_bare_git_dir(struct strbuf *cwd, int offset,
 		gitdir = offset == cwd->len ? "." : xmemdupz(cwd->buf, offset);
 		if (chdir(cwd->buf))
 			die_errno(_("cannot come back to cwd"));
-		return setup_explicit_git_dir(gitdir, cwd, repo_fmt, nongit_ok);
+		return setup_explicit_git_dir(repo, gitdir, cwd, repo_fmt, nongit_ok);
 	}
 
-	inside_git_dir = 1;
-	inside_work_tree = 0;
 	if (offset != cwd->len) {
 		if (chdir(cwd->buf))
 			die_errno(_("cannot come back to cwd"));
 		root_len = offset_1st_component(cwd->buf);
 		strbuf_setlen(cwd, offset > root_len ? offset : root_len);
-		set_git_dir(cwd->buf, 0);
+		set_git_dir(repo, cwd->buf, 0);
 	}
 	else
-		set_git_dir(".", 0);
+		set_git_dir(repo, ".", 0);
 	return NULL;
 }
 
@@ -1485,7 +1489,11 @@ static int allowed_bare_repo_cb(const char *key, const char *value,
 
 static enum allowed_bare_repo get_allowed_bare_repo(void)
 {
+#ifdef WITH_BREAKING_CHANGES
+	enum allowed_bare_repo result = ALLOWED_BARE_REPO_EXPLICIT;
+#else
 	enum allowed_bare_repo result = ALLOWED_BARE_REPO_ALL;
+#endif
 	git_protected_config(allowed_bare_repo_cb, &result);
 	return result;
 }
@@ -1754,7 +1762,38 @@ enum discovery_result discover_git_directory_reason(struct strbuf *commondir,
 	return result;
 }
 
-const char *enter_repo(const char *path, unsigned flags)
+/*
+ * Check the repository format version in the path found in repo_get_git_dir(repo),
+ * and die if it is a version we don't understand. Generally one would
+ * set_git_dir() before calling this, and use it only for "are we in a valid
+ * repo?".
+ *
+ * If successful and fmt is not NULL, fill fmt with data.
+ */
+static void check_repository_format(struct repository *repo, struct repository_format *fmt)
+{
+	struct repository_format repo_fmt = REPOSITORY_FORMAT_INIT;
+	if (!fmt)
+		fmt = &repo_fmt;
+	check_repository_format_gently(repo, repo_get_git_dir(repo), fmt, NULL);
+	startup_info->have_repository = 1;
+	repo_set_hash_algo(repo, fmt->hash_algo);
+	repo_set_compat_hash_algo(repo, fmt->compat_hash_algo);
+	repo_set_ref_storage_format(repo,
+				    fmt->ref_storage_format,
+				    fmt->ref_storage_payload);
+	repo->repository_format_worktree_config =
+		fmt->worktree_config;
+	repo->repository_format_submodule_path_cfg =
+		fmt->submodule_path_cfg;
+	repo->repository_format_relative_worktrees =
+		fmt->relative_worktrees;
+	repo->repository_format_partial_clone =
+		xstrdup_or_null(fmt->partial_clone);
+	clear_repository_format(&repo_fmt);
+}
+
+const char *enter_repo(struct repository *repo, const char *path, unsigned flags)
 {
 	static struct strbuf validated_path = STRBUF_INIT;
 	static struct strbuf used_path = STRBUF_INIT;
@@ -1827,40 +1866,38 @@ const char *enter_repo(const char *path, unsigned flags)
 	}
 
 	if (is_git_directory(".")) {
-		set_git_dir(".", 0);
-		check_repository_format(NULL);
+		set_git_dir(repo, ".", 0);
+		check_repository_format(repo, NULL);
 		return path;
 	}
 
 	return NULL;
 }
 
-static int git_work_tree_initialized;
-
 /*
  * Note.  This works only before you used a work tree.  This was added
  * primarily to support git-clone to work in a new repository it just
  * created, and is not meant to flip between different work trees.
  */
-void set_git_work_tree(const char *new_work_tree)
+void set_git_work_tree(struct repository *repo, const char *new_work_tree)
 {
-	if (git_work_tree_initialized) {
+	if (repo->worktree_initialized) {
 		struct strbuf realpath = STRBUF_INIT;
 
 		strbuf_realpath(&realpath, new_work_tree, 1);
 		new_work_tree = realpath.buf;
-		if (strcmp(new_work_tree, the_repository->worktree))
+		if (strcmp(new_work_tree, repo->worktree))
 			die("internal error: work tree has already been set\n"
 			    "Current worktree: %s\nNew worktree: %s",
-			    the_repository->worktree, new_work_tree);
+			    repo->worktree, new_work_tree);
 		strbuf_release(&realpath);
 		return;
 	}
-	git_work_tree_initialized = 1;
-	repo_set_worktree(the_repository, new_work_tree);
+	repo->worktree_initialized = true;
+	repo_set_worktree(repo, new_work_tree);
 }
 
-const char *setup_git_directory_gently(int *nongit_ok)
+const char *setup_git_directory_gently(struct repository *repo, int *nongit_ok)
 {
 	static struct strbuf cwd = STRBUF_INIT;
 	struct strbuf dir = STRBUF_INIT, gitdir = STRBUF_INIT, report = STRBUF_INIT;
@@ -1875,7 +1912,7 @@ const char *setup_git_directory_gently(int *nongit_ok)
 	 * configuration (including the per-repo config file that we
 	 * ignored previously).
 	 */
-	repo_config_clear(the_repository);
+	repo_config_clear(repo);
 
 	/*
 	 * Let's assume that we are in a git repository.
@@ -1891,18 +1928,18 @@ const char *setup_git_directory_gently(int *nongit_ok)
 
 	switch (setup_git_directory_gently_1(&dir, &gitdir, &report, 1)) {
 	case GIT_DIR_EXPLICIT:
-		prefix = setup_explicit_git_dir(gitdir.buf, &cwd, &repo_fmt, nongit_ok);
+		prefix = setup_explicit_git_dir(repo, gitdir.buf, &cwd, &repo_fmt, nongit_ok);
 		break;
 	case GIT_DIR_DISCOVERED:
 		if (dir.len < cwd.len && chdir(dir.buf))
 			die(_("cannot change to '%s'"), dir.buf);
-		prefix = setup_discovered_git_dir(gitdir.buf, &cwd, dir.len,
+		prefix = setup_discovered_git_dir(repo, gitdir.buf, &cwd, dir.len,
 						  &repo_fmt, nongit_ok);
 		break;
 	case GIT_DIR_BARE:
 		if (dir.len < cwd.len && chdir(dir.buf))
 			die(_("cannot change to '%s'"), dir.buf);
-		prefix = setup_bare_git_dir(&cwd, dir.len, &repo_fmt, nongit_ok);
+		prefix = setup_bare_git_dir(repo, &cwd, dir.len, &repo_fmt, nongit_ok);
 		break;
 	case GIT_DIR_HIT_CEILING:
 		if (!nongit_ok)
@@ -1982,30 +2019,30 @@ const char *setup_git_directory_gently(int *nongit_ok)
 	    startup_info->have_repository ||
 	    /* GIT_DIR_EXPLICIT */
 	    getenv(GIT_DIR_ENVIRONMENT)) {
-		if (!the_repository->gitdir) {
+		if (!repo->gitdir) {
 			const char *gitdir = getenv(GIT_DIR_ENVIRONMENT);
 			if (!gitdir)
 				gitdir = DEFAULT_GIT_DIR_ENVIRONMENT;
-			setup_git_env(gitdir);
+			setup_git_env(repo, gitdir);
 		}
 		if (startup_info->have_repository) {
-			repo_set_hash_algo(the_repository, repo_fmt.hash_algo);
-			repo_set_compat_hash_algo(the_repository,
+			repo_set_hash_algo(repo, repo_fmt.hash_algo);
+			repo_set_compat_hash_algo(repo,
 						  repo_fmt.compat_hash_algo);
-			repo_set_ref_storage_format(the_repository,
+			repo_set_ref_storage_format(repo,
 						    repo_fmt.ref_storage_format,
 						    repo_fmt.ref_storage_payload);
-			the_repository->repository_format_worktree_config =
+			repo->repository_format_worktree_config =
 				repo_fmt.worktree_config;
-			the_repository->repository_format_relative_worktrees =
+			repo->repository_format_relative_worktrees =
 				repo_fmt.relative_worktrees;
-			the_repository->repository_format_submodule_path_cfg =
+			repo->repository_format_submodule_path_cfg =
 				repo_fmt.submodule_path_cfg;
 			/* take ownership of repo_fmt.partial_clone */
-			the_repository->repository_format_partial_clone =
+			repo->repository_format_partial_clone =
 				repo_fmt.partial_clone;
 			repo_fmt.partial_clone = NULL;
-			the_repository->repository_format_precious_objects =
+			repo->repository_format_precious_objects =
 				repo_fmt.precious_objects;
 		}
 	}
@@ -2038,13 +2075,13 @@ const char *setup_git_directory_gently(int *nongit_ok)
 		format = ref_storage_format_by_name(backend);
 		if (format == REF_STORAGE_FORMAT_UNKNOWN)
 			die(_("unknown ref storage format: '%s'"), backend);
-		repo_set_ref_storage_format(the_repository, format, payload);
+		repo_set_ref_storage_format(repo, format, payload);
 
 		free(backend);
 		free(payload);
 	}
 
-	setup_original_cwd();
+	setup_original_cwd(repo);
 
 	strbuf_release(&dir);
 	strbuf_release(&gitdir);
@@ -2105,38 +2142,15 @@ int git_config_perm(const char *var, const char *value)
 	return -(i & 0666);
 }
 
-void check_repository_format(struct repository_format *fmt)
-{
-	struct repository_format repo_fmt = REPOSITORY_FORMAT_INIT;
-	if (!fmt)
-		fmt = &repo_fmt;
-	check_repository_format_gently(repo_get_git_dir(the_repository), fmt, NULL);
-	startup_info->have_repository = 1;
-	repo_set_hash_algo(the_repository, fmt->hash_algo);
-	repo_set_compat_hash_algo(the_repository, fmt->compat_hash_algo);
-	repo_set_ref_storage_format(the_repository,
-				    fmt->ref_storage_format,
-				    fmt->ref_storage_payload);
-	the_repository->repository_format_worktree_config =
-		fmt->worktree_config;
-	the_repository->repository_format_submodule_path_cfg =
-		fmt->submodule_path_cfg;
-	the_repository->repository_format_relative_worktrees =
-		fmt->relative_worktrees;
-	the_repository->repository_format_partial_clone =
-		xstrdup_or_null(fmt->partial_clone);
-	clear_repository_format(&repo_fmt);
-}
-
 /*
  * Returns the "prefix", a path to the current working directory
  * relative to the work tree root, or NULL, if the current working
  * directory is not a strict subdirectory of the work tree root. The
  * prefix always ends with a '/' character.
  */
-const char *setup_git_directory(void)
+const char *setup_git_directory(struct repository *repo)
 {
-	return setup_git_directory_gently(NULL);
+	return setup_git_directory_gently(repo, NULL);
 }
 
 const char *resolve_gitdir_gently(const char *suspect, int *return_error_code)
@@ -2162,12 +2176,26 @@ int daemonize(void)
 	errno = ENOSYS;
 	return -1;
 #else
-	switch (fork()) {
+	pid_t parent_pid = getpid();
+	pid_t child_pid = fork();
+
+	switch (child_pid) {
 		case 0:
+			/*
+			 * We're in the child process, so we take ownership of
+			 * all tempfiles.
+			 */
+			reassign_tempfile_ownership(parent_pid, getpid());
 			break;
 		case -1:
 			die_errno(_("fork failed"));
 		default:
+			/*
+			 * We're in the parent process, so we drop ownership of
+			 * all tempfiles to prevent us from removing them upon
+			 * exit.
+			 */
+			reassign_tempfile_ownership(parent_pid, child_pid);
 			exit(0);
 	}
 	if (setsid() == -1)
@@ -2239,7 +2267,9 @@ const char *get_template_dir(const char *option_template)
 
 #define GIT_DEFAULT_HASH_ENVIRONMENT "GIT_DEFAULT_HASH"
 
-static void copy_templates_1(struct strbuf *path, struct strbuf *template_path,
+static void copy_templates_1(struct repository *repo,
+			     struct strbuf *path,
+			     struct strbuf *template_path,
 			     DIR *dir)
 {
 	size_t path_baselen = path->len;
@@ -2253,7 +2283,7 @@ static void copy_templates_1(struct strbuf *path, struct strbuf *template_path,
 	 * with the way the namespace under .git/ is organized, should
 	 * be really carefully chosen.
 	 */
-	safe_create_dir(the_repository, path->buf, 1);
+	safe_create_dir(repo, path->buf, 1);
 	while ((de = readdir(dir)) != NULL) {
 		struct stat st_git, st_template;
 		int exists = 0;
@@ -2281,7 +2311,7 @@ static void copy_templates_1(struct strbuf *path, struct strbuf *template_path,
 				die_errno(_("cannot opendir '%s'"), template_path->buf);
 			strbuf_addch(path, '/');
 			strbuf_addch(template_path, '/');
-			copy_templates_1(path, template_path, subdir);
+			copy_templates_1(repo, path, template_path, subdir);
 			closedir(subdir);
 		}
 		else if (exists)
@@ -2306,7 +2336,7 @@ static void copy_templates_1(struct strbuf *path, struct strbuf *template_path,
 	}
 }
 
-static void copy_templates(const char *option_template)
+static void copy_templates(struct repository *repo, const char *option_template)
 {
 	const char *template_dir = get_template_dir(option_template);
 	struct strbuf path = STRBUF_INIT;
@@ -2347,9 +2377,9 @@ static void copy_templates(const char *option_template)
 		goto close_free_return;
 	}
 
-	strbuf_addstr(&path, repo_get_common_dir(the_repository));
+	strbuf_addstr(&path, repo_get_common_dir(repo));
 	strbuf_complete(&path, '/');
-	copy_templates_1(&path, &template_path, dir);
+	copy_templates_1(repo, &path, &template_path, dir);
 close_free_return:
 	closedir(dir);
 free_return:
@@ -2373,7 +2403,8 @@ static int needs_work_tree_config(const char *git_dir, const char *work_tree)
 	return 1;
 }
 
-void initialize_repository_version(int hash_algo,
+void initialize_repository_version(struct repository *repo,
+				   int hash_algo,
 				   enum ref_storage_format ref_storage_format,
 				   int reinit)
 {
@@ -2390,35 +2421,35 @@ void initialize_repository_version(int hash_algo,
 	 */
 	if (hash_algo != GIT_HASH_SHA1_LEGACY ||
 	    ref_storage_format != REF_STORAGE_FORMAT_FILES ||
-	    the_repository->ref_storage_payload)
+	    repo->ref_storage_payload)
 		target_version = GIT_REPO_VERSION_READ;
 
 	if (hash_algo != GIT_HASH_SHA1_LEGACY && hash_algo != GIT_HASH_UNKNOWN)
-		repo_config_set(the_repository, "extensions.objectformat",
+		repo_config_set(repo, "extensions.objectformat",
 				hash_algos[hash_algo].name);
 	else if (reinit)
-		repo_config_set_gently(the_repository, "extensions.objectformat", NULL);
+		repo_config_set_gently(repo, "extensions.objectformat", NULL);
 
-	if (the_repository->ref_storage_payload) {
+	if (repo->ref_storage_payload) {
 		struct strbuf ref_uri = STRBUF_INIT;
 
 		strbuf_addf(&ref_uri, "%s://%s",
 			    ref_storage_format_to_name(ref_storage_format),
-			    the_repository->ref_storage_payload);
-		repo_config_set(the_repository, "extensions.refstorage", ref_uri.buf);
+			    repo->ref_storage_payload);
+		repo_config_set(repo, "extensions.refstorage", ref_uri.buf);
 		strbuf_release(&ref_uri);
 	} else if (ref_storage_format != REF_STORAGE_FORMAT_FILES) {
-		repo_config_set(the_repository, "extensions.refstorage",
+		repo_config_set(repo, "extensions.refstorage",
 				ref_storage_format_to_name(ref_storage_format));
 	} else if (reinit) {
-		repo_config_set_gently(the_repository, "extensions.refstorage", NULL);
+		repo_config_set_gently(repo, "extensions.refstorage", NULL);
 	}
 
 	if (reinit) {
 		struct strbuf config = STRBUF_INIT;
 		struct repository_format repo_fmt = REPOSITORY_FORMAT_INIT;
 
-		repo_common_path_append(the_repository, &config, "config");
+		repo_common_path_append(repo, &config, "config");
 		read_repository_format(&repo_fmt, config.buf);
 
 		if (repo_fmt.v1_only_extensions.nr)
@@ -2428,40 +2459,41 @@ void initialize_repository_version(int hash_algo,
 		clear_repository_format(&repo_fmt);
 	}
 
-	repo_config_get_bool(the_repository, "init.defaultSubmodulePathConfig",
+	repo_config_get_bool(repo, "init.defaultSubmodulePathConfig",
 			     &default_submodule_path_config);
 	if (default_submodule_path_config) {
 		/* extensions.submodulepathconfig requires at least version 1 */
 		if (target_version == 0)
 			target_version = 1;
-		repo_config_set(the_repository, "extensions.submodulepathconfig", "true");
+		repo_config_set(repo, "extensions.submodulepathconfig", "true");
 	}
 
 	strbuf_addf(&repo_version, "%d", target_version);
-	repo_config_set(the_repository, "core.repositoryformatversion", repo_version.buf);
+	repo_config_set(repo, "core.repositoryformatversion", repo_version.buf);
 
 	strbuf_release(&repo_version);
 }
 
-static int is_reinit(void)
+static int is_reinit(struct repository *repo)
 {
 	struct strbuf buf = STRBUF_INIT;
 	char junk[2];
 	int ret;
 
-	repo_git_path_replace(the_repository, &buf, "HEAD");
+	repo_git_path_replace(repo, &buf, "HEAD");
 	ret = !access(buf.buf, R_OK) || readlink(buf.buf, junk, sizeof(junk) - 1) != -1;
 	strbuf_release(&buf);
 	return ret;
 }
 
-void create_reference_database(const char *initial_branch, int quiet)
+void create_reference_database(struct repository *repo,
+			       const char *initial_branch, int quiet)
 {
 	struct strbuf err = STRBUF_INIT;
 	char *to_free = NULL;
-	int reinit = is_reinit();
+	int reinit = is_reinit(repo);
 
-	if (ref_store_create_on_disk(get_main_ref_store(the_repository), 0, &err))
+	if (ref_store_create_on_disk(get_main_ref_store(repo), 0, &err))
 		die("failed to set up refs db: %s", err.buf);
 
 	/*
@@ -2473,14 +2505,14 @@ void create_reference_database(const char *initial_branch, int quiet)
 
 		if (!initial_branch)
 			initial_branch = to_free =
-				repo_default_branch_name(the_repository, quiet);
+				repo_default_branch_name(repo, quiet);
 
 		ref = xstrfmt("refs/heads/%s", initial_branch);
 		if (check_refname_format(ref, 0) < 0)
 			die(_("invalid initial branch name: '%s'"),
 			    initial_branch);
 
-		if (refs_update_symref(get_main_ref_store(the_repository), "HEAD", ref, NULL) < 0)
+		if (refs_update_symref(get_main_ref_store(repo), "HEAD", ref, NULL) < 0)
 			exit(1);
 		free(ref);
 	}
@@ -2493,7 +2525,8 @@ void create_reference_database(const char *initial_branch, int quiet)
 	free(to_free);
 }
 
-static int create_default_files(const char *template_path,
+static int create_default_files(struct repository *repo,
+				const char *template_path,
 				const char *original_git_dir,
 				const struct repository_format *fmt,
 				int init_shared_repository)
@@ -2502,7 +2535,7 @@ static int create_default_files(const char *template_path,
 	struct strbuf path = STRBUF_INIT;
 	int reinit;
 	int filemode;
-	const char *work_tree = repo_get_work_tree(the_repository);
+	const char *work_tree = repo_get_work_tree(repo);
 
 	/*
 	 * First copy the templates -- we might have the default
@@ -2513,19 +2546,19 @@ static int create_default_files(const char *template_path,
 	 * values (since we've just potentially changed what's available on
 	 * disk).
 	 */
-	copy_templates(template_path);
-	repo_config_clear(the_repository);
-	repo_settings_reset_shared_repository(the_repository);
-	repo_config(the_repository, git_default_config, NULL);
+	copy_templates(repo, template_path);
+	repo_config_clear(repo);
+	repo_settings_reset_shared_repository(repo);
+	repo_config(repo, git_default_config, NULL);
 
-	reinit = is_reinit();
+	reinit = is_reinit(repo);
 
 	/*
 	 * We must make sure command-line options continue to override any
 	 * values we might have just re-read from the config.
 	 */
 	if (init_shared_repository != -1)
-		repo_settings_set_shared_repository(the_repository,
+		repo_settings_set_shared_repository(repo,
 						    init_shared_repository);
 
 	is_bare_repository_cfg = !work_tree;
@@ -2534,14 +2567,14 @@ static int create_default_files(const char *template_path,
 	 * We would have created the above under user's umask -- under
 	 * shared-repository settings, we would need to fix them up.
 	 */
-	if (repo_settings_get_shared_repository(the_repository)) {
-		adjust_shared_perm(the_repository, repo_get_git_dir(the_repository));
+	if (repo_settings_get_shared_repository(repo)) {
+		adjust_shared_perm(repo, repo_get_git_dir(repo));
 	}
 
-	initialize_repository_version(fmt->hash_algo, fmt->ref_storage_format, reinit);
+	initialize_repository_version(repo, fmt->hash_algo, fmt->ref_storage_format, reinit);
 
 	/* Check filemode trustability */
-	repo_git_path_replace(the_repository, &path, "config");
+	repo_git_path_replace(repo, &path, "config");
 	filemode = TEST_FILEMODE;
 	if (TEST_FILEMODE && !lstat(path.buf, &st1)) {
 		struct stat st2;
@@ -2552,22 +2585,22 @@ static int create_default_files(const char *template_path,
 		if (filemode && !reinit && (st1.st_mode & S_IXUSR))
 			filemode = 0;
 	}
-	repo_config_set(the_repository, "core.filemode", filemode ? "true" : "false");
+	repo_config_set(repo, "core.filemode", filemode ? "true" : "false");
 
 	if (is_bare_repository())
-		repo_config_set(the_repository, "core.bare", "true");
+		repo_config_set(repo, "core.bare", "true");
 	else {
-		repo_config_set(the_repository, "core.bare", "false");
+		repo_config_set(repo, "core.bare", "false");
 		/* allow template config file to override the default */
-		if (repo_settings_get_log_all_ref_updates(the_repository) == LOG_REFS_UNSET)
-			repo_config_set(the_repository, "core.logallrefupdates", "true");
+		if (repo_settings_get_log_all_ref_updates(repo) == LOG_REFS_UNSET)
+			repo_config_set(repo, "core.logallrefupdates", "true");
 		if (needs_work_tree_config(original_git_dir, work_tree))
-			repo_config_set(the_repository, "core.worktree", work_tree);
+			repo_config_set(repo, "core.worktree", work_tree);
 	}
 
 	if (!reinit) {
 		/* Check if symlink is supported in the work tree */
-		repo_git_path_replace(the_repository, &path, "tXXXXXX");
+		repo_git_path_replace(repo, &path, "tXXXXXX");
 		if (!close(xmkstemp(path.buf)) &&
 		    !unlink(path.buf) &&
 		    !symlink("testing", path.buf) &&
@@ -2575,12 +2608,12 @@ static int create_default_files(const char *template_path,
 		    S_ISLNK(st1.st_mode))
 			unlink(path.buf); /* good */
 		else
-			repo_config_set(the_repository, "core.symlinks", "false");
+			repo_config_set(repo, "core.symlinks", "false");
 
 		/* Check if the filesystem is case-insensitive */
-		repo_git_path_replace(the_repository, &path, "CoNfIg");
+		repo_git_path_replace(repo, &path, "CoNfIg");
 		if (!access(path.buf, F_OK))
-			repo_config_set(the_repository, "core.ignorecase", "true");
+			repo_config_set(repo, "core.ignorecase", "true");
 		probe_utf8_pathname_composition();
 	}
 
@@ -2588,23 +2621,23 @@ static int create_default_files(const char *template_path,
 	return reinit;
 }
 
-static void create_object_directory(void)
+static void create_object_directory(struct repository *repo)
 {
 	struct strbuf path = STRBUF_INIT;
 	size_t baselen;
 
-	strbuf_addstr(&path, repo_get_object_directory(the_repository));
+	strbuf_addstr(&path, repo_get_object_directory(repo));
 	baselen = path.len;
 
-	safe_create_dir(the_repository, path.buf, 1);
+	safe_create_dir(repo, path.buf, 1);
 
 	strbuf_setlen(&path, baselen);
 	strbuf_addstr(&path, "/pack");
-	safe_create_dir(the_repository, path.buf, 1);
+	safe_create_dir(repo, path.buf, 1);
 
 	strbuf_setlen(&path, baselen);
 	strbuf_addstr(&path, "/info");
-	safe_create_dir(the_repository, path.buf, 1);
+	safe_create_dir(repo, path.buf, 1);
 
 	strbuf_release(&path);
 }
@@ -2682,7 +2715,8 @@ static int read_default_format_config(const char *key, const char *value,
 	return ret;
 }
 
-static void repository_format_configure(struct repository_format *repo_fmt,
+static void repository_format_configure(struct repository *repo,
+					struct repository_format *repo_fmt,
 					int hash, enum ref_storage_format ref_format)
 {
 	struct default_format_config cfg = {
@@ -2719,7 +2753,7 @@ static void repository_format_configure(struct repository_format *repo_fmt,
 	} else if (cfg.hash != GIT_HASH_UNKNOWN) {
 		repo_fmt->hash_algo = cfg.hash;
 	}
-	repo_set_hash_algo(the_repository, repo_fmt->hash_algo);
+	repo_set_hash_algo(repo, repo_fmt->hash_algo);
 
 	env = getenv("GIT_DEFAULT_REF_FORMAT");
 	if (repo_fmt->version >= 0 &&
@@ -2758,11 +2792,12 @@ static void repository_format_configure(struct repository_format *repo_fmt,
 		free(backend);
 	}
 
-	repo_set_ref_storage_format(the_repository, repo_fmt->ref_storage_format,
+	repo_set_ref_storage_format(repo, repo_fmt->ref_storage_format,
 				    repo_fmt->ref_storage_payload);
 }
 
-int init_db(const char *git_dir, const char *real_git_dir,
+int init_db(struct repository *repo,
+	    const char *git_dir, const char *real_git_dir,
 	    const char *template_dir, int hash,
 	    enum ref_storage_format ref_storage_format,
 	    const char *initial_branch,
@@ -2782,13 +2817,13 @@ int init_db(const char *git_dir, const char *real_git_dir,
 		if (!exist_ok && !stat(real_git_dir, &st))
 			die(_("%s already exists"), real_git_dir);
 
-		set_git_dir(real_git_dir, 1);
-		git_dir = repo_get_git_dir(the_repository);
+		set_git_dir(repo, real_git_dir, 1);
+		git_dir = repo_get_git_dir(repo);
 		separate_git_dir(git_dir, original_git_dir);
 	}
 	else {
-		set_git_dir(git_dir, 1);
-		git_dir = repo_get_git_dir(the_repository);
+		set_git_dir(repo, git_dir, 1);
+		git_dir = repo_get_git_dir(repo);
 	}
 	startup_info->have_repository = 1;
 
@@ -2798,27 +2833,27 @@ int init_db(const char *git_dir, const char *real_git_dir,
 	 * config file, so this will not fail.  What we are catching
 	 * is an attempt to reinitialize new repository with an old tool.
 	 */
-	check_repository_format(&repo_fmt);
+	check_repository_format(repo, &repo_fmt);
 
-	repository_format_configure(&repo_fmt, hash, ref_storage_format);
+	repository_format_configure(repo, &repo_fmt, hash, ref_storage_format);
 
 	/*
 	 * Ensure `core.hidedotfiles` is processed. This must happen after we
 	 * have set up the repository format such that we can evaluate
 	 * includeIf conditions correctly in the case of re-initialization.
 	 */
-	repo_config(the_repository, git_default_core_config, NULL);
+	repo_config(repo, git_default_core_config, NULL);
 
-	safe_create_dir(the_repository, git_dir, 0);
+	safe_create_dir(repo, git_dir, 0);
 
-	reinit = create_default_files(template_dir, original_git_dir,
+	reinit = create_default_files(repo, template_dir, original_git_dir,
 				      &repo_fmt, init_shared_repository);
 
 	if (!(flags & INIT_DB_SKIP_REFDB))
-		create_reference_database(initial_branch, flags & INIT_DB_QUIET);
-	create_object_directory();
+		create_reference_database(repo, initial_branch, flags & INIT_DB_QUIET);
+	create_object_directory(repo);
 
-	if (repo_settings_get_shared_repository(the_repository)) {
+	if (repo_settings_get_shared_repository(repo)) {
 		char buf[10];
 		/* We do not spell "group" and such, so that
 		 * the configuration can be read by older version
@@ -2826,29 +2861,29 @@ int init_db(const char *git_dir, const char *real_git_dir,
 		 * and compatibility values for PERM_GROUP and
 		 * PERM_EVERYBODY.
 		 */
-		if (repo_settings_get_shared_repository(the_repository) < 0)
+		if (repo_settings_get_shared_repository(repo) < 0)
 			/* force to the mode value */
-			xsnprintf(buf, sizeof(buf), "0%o", -repo_settings_get_shared_repository(the_repository));
-		else if (repo_settings_get_shared_repository(the_repository) == PERM_GROUP)
+			xsnprintf(buf, sizeof(buf), "0%o", -repo_settings_get_shared_repository(repo));
+		else if (repo_settings_get_shared_repository(repo) == PERM_GROUP)
 			xsnprintf(buf, sizeof(buf), "%d", OLD_PERM_GROUP);
-		else if (repo_settings_get_shared_repository(the_repository) == PERM_EVERYBODY)
+		else if (repo_settings_get_shared_repository(repo) == PERM_EVERYBODY)
 			xsnprintf(buf, sizeof(buf), "%d", OLD_PERM_EVERYBODY);
 		else
 			BUG("invalid value for shared_repository");
-		repo_config_set(the_repository, "core.sharedrepository", buf);
-		repo_config_set(the_repository, "receive.denyNonFastforwards", "true");
+		repo_config_set(repo, "core.sharedrepository", buf);
+		repo_config_set(repo, "receive.denyNonFastforwards", "true");
 	}
 
 	if (!(flags & INIT_DB_QUIET)) {
 		int len = strlen(git_dir);
 
 		if (reinit)
-			printf(repo_settings_get_shared_repository(the_repository)
+			printf(repo_settings_get_shared_repository(repo)
 			       ? _("Reinitialized existing shared Git repository in %s%s\n")
 			       : _("Reinitialized existing Git repository in %s%s\n"),
 			       git_dir, len && git_dir[len-1] != '/' ? "/" : "");
 		else
-			printf(repo_settings_get_shared_repository(the_repository)
+			printf(repo_settings_get_shared_repository(repo)
 			       ? _("Initialized empty shared Git repository in %s%s\n")
 			       : _("Initialized empty Git repository in %s%s\n"),
 			       git_dir, len && git_dir[len-1] != '/' ? "/" : "");
diff --git a/setup.h b/setup.h
index 80bc6e5..7878c9d 100644
--- a/setup.h
+++ b/setup.h
@@ -4,8 +4,8 @@
 #include "refs.h"
 #include "string-list.h"
 
-int is_inside_git_dir(void);
-int is_inside_work_tree(void);
+int is_inside_git_dir(struct repository *repo);
+int is_inside_work_tree(struct repository *repo);
 int get_common_dir_noenv(struct strbuf *sb, const char *gitdir);
 int get_common_dir(struct strbuf *sb, const char *gitdir);
 
@@ -56,7 +56,7 @@ const char *resolve_gitdir_gently(const char *suspect, int *return_error_code);
 void die_upon_dubious_ownership(const char *gitfile, const char *worktree,
 				const char *gitdir);
 
-void setup_work_tree(void);
+void setup_work_tree(struct repository *repo);
 
 /*
  * discover_git_directory_reason() is similar to discover_git_directory(),
@@ -96,7 +96,7 @@ static inline int discover_git_directory(struct strbuf *commondir,
 	return 0;
 }
 
-void set_git_work_tree(const char *tree);
+void set_git_work_tree(struct repository *repo, const char *tree);
 
 /* Flags that can be passed to `enter_repo()`. */
 enum {
@@ -134,21 +134,37 @@ enum {
  * links.  User relative paths are also returned as they are given,
  * except DWIM suffixing.
  */
-const char *enter_repo(const char *path, unsigned flags);
+const char *enter_repo(struct repository *repo, const char *path, unsigned flags);
 
-const char *setup_git_directory_gently(int *);
-const char *setup_git_directory(void);
-char *prefix_path(const char *prefix, int len, const char *path);
-char *prefix_path_gently(const char *prefix, int len, int *remaining, const char *path);
+const char *setup_git_directory_gently(struct repository *repo, int *);
+const char *setup_git_directory(struct repository *repo);
+char *prefix_path(struct repository *repo, const char *prefix, int len, const char *path);
+char *prefix_path_gently(struct repository *repo, const char *prefix, int len, int *remaining, const char *path);
 
 int check_filename(const char *prefix, const char *name);
-void verify_filename(const char *prefix,
+void verify_filename(struct repository *repo,
+		     const char *prefix,
 		     const char *name,
 		     int diagnose_misspelt_rev);
-void verify_non_filename(const char *prefix, const char *name);
-int path_inside_repo(const char *prefix, const char *path);
+void verify_non_filename(struct repository *repo, const char *prefix, const char *name);
+int path_inside_repo(struct repository *repo, const char *prefix, const char *path);
 
 void sanitize_stdfds(void);
+
+/*
+ * Daemonize the current process by forking and then exiting the parent
+ * process. Returns 0 when successful, in which case the parent process will
+ * have exited and it's the child process that continues to run the code.
+ * Otherwise, a negative error code is returned and the parent process will
+ * continue execution.
+ *
+ * Note that this function will also perform the following changes:
+ *
+ *   - Standard file descriptors in the child process are closed.
+ *   - The child process is made a session leader via setsid(3p).
+ *   - All tempfiles owned by the parent process are reassigned to the
+ *     daemonized child process.
+ */
 int daemonize(void);
 
 /*
@@ -220,31 +236,23 @@ void clear_repository_format(struct repository_format *format);
 int verify_repository_format(const struct repository_format *format,
 			     struct strbuf *err);
 
-/*
- * Check the repository format version in the path found in repo_get_git_dir(the_repository),
- * and die if it is a version we don't understand. Generally one would
- * set_git_dir() before calling this, and use it only for "are we in a valid
- * repo?".
- *
- * If successful and fmt is not NULL, fill fmt with data.
- */
-void check_repository_format(struct repository_format *fmt);
-
 const char *get_template_dir(const char *option_template);
 
 #define INIT_DB_QUIET      (1 << 0)
 #define INIT_DB_EXIST_OK   (1 << 1)
 #define INIT_DB_SKIP_REFDB (1 << 2)
 
-int init_db(const char *git_dir, const char *real_git_dir,
+int init_db(struct repository *repo,
+	    const char *git_dir, const char *real_git_dir,
 	    const char *template_dir, int hash_algo,
 	    enum ref_storage_format ref_storage_format,
 	    const char *initial_branch, int init_shared_repository,
 	    unsigned int flags);
-void initialize_repository_version(int hash_algo,
+void initialize_repository_version(struct repository *repo,
+				   int hash_algo,
 				   enum ref_storage_format ref_storage_format,
 				   int reinit);
-void create_reference_database(const char *initial_branch, int quiet);
+void create_reference_database(struct repository *repo, const char *initial_branch, int quiet);
 
 /*
  * NOTE NOTE NOTE!!
diff --git a/shallow.c b/shallow.c
index 7a3dd56..610ff3d 100644
--- a/shallow.c
+++ b/shallow.c
@@ -245,7 +245,11 @@ struct commit_list *get_shallow_commits(struct object_array *heads,
 					int depth, int shallow_flag, int not_shallow_flag)
 {
 	if (shallows && deepen_relative) {
-		depth += get_shallows_depth(heads, shallows);
+		int cur_shallow_depth = get_shallows_depth(heads, shallows);
+		if (cur_shallow_depth)
+			depth += cur_shallow_depth;
+		else
+			return NULL;
 	}
 	return get_shallows_or_depth(heads, NULL, NULL,
 				     depth, shallow_flag, not_shallow_flag);
@@ -360,7 +364,7 @@ static int write_one_shallow(const struct commit_graft *graft, void *cb_data)
 		return 0;
 	if (data->flags & QUICK) {
 		if (!odb_has_object(the_repository->objects, &graft->oid,
-				    HAS_OBJECT_RECHECK_PACKED | HAS_OBJECT_FETCH_PROMISOR))
+				    ODB_HAS_OBJECT_RECHECK_PACKED | ODB_HAS_OBJECT_FETCH_PROMISOR))
 			return 0;
 	} else if (data->flags & SEEN_ONLY) {
 		struct commit *c = lookup_commit(the_repository, &graft->oid);
@@ -528,7 +532,7 @@ void prepare_shallow_info(struct shallow_info *info, struct oid_array *sa)
 	ALLOC_ARRAY(info->theirs, sa->nr);
 	for (size_t i = 0; i < sa->nr; i++) {
 		if (odb_has_object(the_repository->objects, sa->oid + i,
-				   HAS_OBJECT_RECHECK_PACKED | HAS_OBJECT_FETCH_PROMISOR)) {
+				   ODB_HAS_OBJECT_RECHECK_PACKED | ODB_HAS_OBJECT_FETCH_PROMISOR)) {
 			struct commit_graft *graft;
 			graft = lookup_commit_graft(the_repository,
 						    &sa->oid[i]);
@@ -567,7 +571,7 @@ void remove_nonexistent_theirs_shallow(struct shallow_info *info)
 		if (i != dst)
 			info->theirs[dst] = info->theirs[i];
 		if (odb_has_object(the_repository->objects, oid + info->theirs[i],
-				   HAS_OBJECT_RECHECK_PACKED | HAS_OBJECT_FETCH_PROMISOR))
+				   ODB_HAS_OBJECT_RECHECK_PACKED | ODB_HAS_OBJECT_FETCH_PROMISOR))
 			dst++;
 	}
 	info->nr_theirs = dst;
diff --git a/sideband.c b/sideband.c
index 1ed6614..1523a53 100644
--- a/sideband.c
+++ b/sideband.c
@@ -10,6 +10,7 @@
 #include "help.h"
 #include "pkt-line.h"
 #include "write-or-die.h"
+#include "urlmatch.h"
 
 struct keyword_entry {
 	/*
@@ -26,6 +27,85 @@ static struct keyword_entry keywords[] = {
 	{ "error",	GIT_COLOR_BOLD_RED },
 };
 
+static enum {
+	ALLOW_CONTROL_SEQUENCES_UNSET = -1,
+	ALLOW_NO_CONTROL_CHARACTERS   = 0,
+	ALLOW_ANSI_COLOR_SEQUENCES    = 1<<0,
+	ALLOW_ANSI_CURSOR_MOVEMENTS   = 1<<1,
+	ALLOW_ANSI_ERASE              = 1<<2,
+	ALLOW_ALL_CONTROL_CHARACTERS  = 1<<3,
+	ALLOW_DEFAULT_ANSI_SEQUENCES  = ALLOW_ANSI_COLOR_SEQUENCES
+} allow_control_characters = ALLOW_CONTROL_SEQUENCES_UNSET;
+
+static inline int skip_prefix_in_csv(const char *value, const char *prefix,
+				     const char **out)
+{
+	if (!skip_prefix(value, prefix, &value) ||
+	    (*value && *value != ','))
+		return 0;
+	*out = value + !!*value;
+	return 1;
+}
+
+int sideband_allow_control_characters_config(const char *var, const char *value)
+{
+	switch (git_parse_maybe_bool(value)) {
+	case 0:
+		allow_control_characters = ALLOW_NO_CONTROL_CHARACTERS;
+		return 0;
+	case 1:
+		allow_control_characters = ALLOW_ALL_CONTROL_CHARACTERS;
+		return 0;
+	default:
+		break;
+	}
+
+	allow_control_characters = ALLOW_NO_CONTROL_CHARACTERS;
+	while (*value) {
+		if (skip_prefix_in_csv(value, "color", &value))
+			allow_control_characters |= ALLOW_ANSI_COLOR_SEQUENCES;
+		else if (skip_prefix_in_csv(value, "cursor", &value))
+			allow_control_characters |= ALLOW_ANSI_CURSOR_MOVEMENTS;
+		else if (skip_prefix_in_csv(value, "erase", &value))
+			allow_control_characters |= ALLOW_ANSI_ERASE;
+		else if (skip_prefix_in_csv(value, "true", &value))
+			allow_control_characters = ALLOW_ALL_CONTROL_CHARACTERS;
+		else if (skip_prefix_in_csv(value, "false", &value))
+			allow_control_characters = ALLOW_NO_CONTROL_CHARACTERS;
+		else
+			warning(_("unrecognized value for '%s': '%s'"), var, value);
+	}
+	return 0;
+}
+
+static int sideband_config_callback(const char *var, const char *value,
+				    const struct config_context *ctx UNUSED,
+				    void *data UNUSED)
+{
+	if (!strcmp(var, "sideband.allowcontrolcharacters"))
+		return sideband_allow_control_characters_config(var, value);
+
+	return 0;
+}
+
+void sideband_apply_url_config(const char *url)
+{
+	struct urlmatch_config config = URLMATCH_CONFIG_INIT;
+	char *normalized_url;
+
+	if (!url)
+		BUG("must not call sideband_apply_url_config(NULL)");
+
+	config.section = "sideband";
+	config.collect_fn = sideband_config_callback;
+
+	normalized_url = url_normalize(url, &config.url);
+	repo_config(the_repository, urlmatch_config_entry, &config);
+	free(normalized_url);
+	string_list_clear(&config.vars, 1);
+	urlmatch_config_release(&config);
+}
+
 /* Returns a color setting (GIT_COLOR_NEVER, etc). */
 static enum git_colorbool use_sideband_colors(void)
 {
@@ -39,6 +119,14 @@ static enum git_colorbool use_sideband_colors(void)
 	if (use_sideband_colors_cached != GIT_COLOR_UNKNOWN)
 		return use_sideband_colors_cached;
 
+	if (allow_control_characters == ALLOW_CONTROL_SEQUENCES_UNSET) {
+		if (!repo_config_get_value(the_repository, "sideband.allowcontrolcharacters", &value))
+			sideband_allow_control_characters_config("sideband.allowcontrolcharacters", value);
+
+		if (allow_control_characters == ALLOW_CONTROL_SEQUENCES_UNSET)
+			allow_control_characters = ALLOW_DEFAULT_ANSI_SEQUENCES;
+	}
+
 	if (!repo_config_get_string_tmp(the_repository, key, &value))
 		use_sideband_colors_cached = git_config_colorbool(key, value);
 	else if (!repo_config_get_string_tmp(the_repository, "color.ui", &value))
@@ -66,6 +154,93 @@ void list_config_color_sideband_slots(struct string_list *list, const char *pref
 		list_config_item(list, prefix, keywords[i].keyword);
 }
 
+static int handle_ansi_sequence(struct strbuf *dest, const char *src, int n)
+{
+	int i;
+
+	/*
+	 * Valid ANSI color sequences are of the form
+	 *
+	 * ESC [ [<n> [; <n>]*] m
+	 *
+	 * These are part of the Select Graphic Rendition sequences which
+	 * contain more than just color sequences, for more details see
+	 * https://en.wikipedia.org/wiki/ANSI_escape_code#SGR.
+	 *
+	 * The cursor movement sequences are:
+	 *
+	 * ESC [ n A - Cursor up n lines (CUU)
+	 * ESC [ n B - Cursor down n lines (CUD)
+	 * ESC [ n C - Cursor forward n columns (CUF)
+	 * ESC [ n D - Cursor back n columns (CUB)
+	 * ESC [ n E - Cursor next line, beginning (CNL)
+	 * ESC [ n F - Cursor previous line, beginning (CPL)
+	 * ESC [ n G - Cursor to column n (CHA)
+	 * ESC [ n ; m H - Cursor position (row n, col m) (CUP)
+	 * ESC [ n ; m f - Same as H (HVP)
+	 *
+	 * The sequences to erase characters are:
+	 *
+	 *
+	 * ESC [ 0 J - Clear from cursor to end of screen (ED)
+	 * ESC [ 1 J - Clear from cursor to beginning of screen (ED)
+	 * ESC [ 2 J - Clear entire screen (ED)
+	 * ESC [ 3 J - Clear entire screen + scrollback (ED) - xterm extension
+	 * ESC [ 0 K - Clear from cursor to end of line (EL)
+	 * ESC [ 1 K - Clear from cursor to beginning of line (EL)
+	 * ESC [ 2 K - Clear entire line (EL)
+	 * ESC [ n M - Delete n lines (DL)
+	 * ESC [ n P - Delete n characters (DCH)
+	 * ESC [ n X - Erase n characters (ECH)
+	 *
+	 * For a comprehensive list of common ANSI Escape sequences, see
+	 * https://www.xfree86.org/current/ctlseqs.html
+	 */
+
+	if (n < 3 || src[0] != '\x1b' || src[1] != '[')
+		return 0;
+
+	for (i = 2; i < n; i++) {
+		if (((allow_control_characters & ALLOW_ANSI_COLOR_SEQUENCES) &&
+		     src[i] == 'm') ||
+		    ((allow_control_characters & ALLOW_ANSI_CURSOR_MOVEMENTS) &&
+		     strchr("ABCDEFGHf", src[i])) ||
+		    ((allow_control_characters & ALLOW_ANSI_ERASE) &&
+		     strchr("JKMPX", src[i]))) {
+			strbuf_add(dest, src, i + 1);
+			return i;
+		}
+		if (!isdigit(src[i]) && src[i] != ';')
+			break;
+	}
+
+	return 0;
+}
+
+static void strbuf_add_sanitized(struct strbuf *dest, const char *src, int n)
+{
+	int i;
+
+	if ((allow_control_characters & ALLOW_ALL_CONTROL_CHARACTERS)) {
+		strbuf_add(dest, src, n);
+		return;
+	}
+
+	strbuf_grow(dest, n);
+	for (; n && *src; src++, n--) {
+		if (!iscntrl(*src) || *src == '\t' || *src == '\n') {
+			strbuf_addch(dest, *src);
+		} else if (allow_control_characters != ALLOW_NO_CONTROL_CHARACTERS &&
+			   (i = handle_ansi_sequence(dest, src, n))) {
+			src += i;
+			n -= i;
+		} else {
+			strbuf_addch(dest, '^');
+			strbuf_addch(dest, *src == 0x7f ? '?' : 0x40 + *src);
+		}
+	}
+}
+
 /*
  * Optionally highlight one keyword in remote output if it appears at the start
  * of the line. This should be called for a single line only, which is
@@ -81,7 +256,7 @@ static void maybe_colorize_sideband(struct strbuf *dest, const char *src, int n)
 	int i;
 
 	if (!want_color_stderr(use_sideband_colors())) {
-		strbuf_add(dest, src, n);
+		strbuf_add_sanitized(dest, src, n);
 		return;
 	}
 
@@ -114,13 +289,13 @@ static void maybe_colorize_sideband(struct strbuf *dest, const char *src, int n)
 		}
 	}
 
-	strbuf_add(dest, src, n);
+	strbuf_add_sanitized(dest, src, n);
 }
 
 
 #define DISPLAY_PREFIX "remote: "
 
-#define ANSI_SUFFIX "\033[K"
+#define ANSI_PREFIX "\033[K"
 #define DUMB_SUFFIX "        "
 
 int demultiplex_sideband(const char *me, int status,
@@ -129,15 +304,18 @@ int demultiplex_sideband(const char *me, int status,
 			 struct strbuf *scratch,
 			 enum sideband_type *sideband_type)
 {
-	static const char *suffix;
+	static const char *prefix, *suffix;
 	const char *b, *brk;
 	int band;
 
 	if (!suffix) {
-		if (isatty(2) && !is_terminal_dumb())
-			suffix = ANSI_SUFFIX;
-		else
+		if (isatty(2) && !is_terminal_dumb()) {
+			prefix = ANSI_PREFIX DISPLAY_PREFIX;
+			suffix = "";
+		} else {
+			prefix = DISPLAY_PREFIX;
 			suffix = DUMB_SUFFIX;
+		}
 	}
 
 	if (status == PACKET_READ_EOF) {
@@ -171,8 +349,7 @@ int demultiplex_sideband(const char *me, int status,
 	case 3:
 		if (die_on_error)
 			die(_("remote error: %s"), buf + 1);
-		strbuf_addf(scratch, "%s%s", scratch->len ? "\n" : "",
-			    DISPLAY_PREFIX);
+		strbuf_addf(scratch, "%s%s", scratch->len ? "\n" : "", prefix);
 		maybe_colorize_sideband(scratch, buf + 1, len);
 
 		*sideband_type = SIDEBAND_REMOTE_ERROR;
@@ -203,7 +380,7 @@ int demultiplex_sideband(const char *me, int status,
 				strbuf_addstr(scratch, suffix);
 
 			if (!scratch->len)
-				strbuf_addstr(scratch, DISPLAY_PREFIX);
+				strbuf_addstr(scratch, prefix);
 
 			/*
 			 * A use case that we should not add clear-to-eol suffix
@@ -229,8 +406,8 @@ int demultiplex_sideband(const char *me, int status,
 		}
 
 		if (*b) {
-			strbuf_addstr(scratch, scratch->len ?
-				    "" : DISPLAY_PREFIX);
+			if (!scratch->len)
+				strbuf_addstr(scratch, prefix);
 			maybe_colorize_sideband(scratch, b, strlen(b));
 		}
 		return 0;
@@ -264,7 +441,6 @@ void send_sideband(int fd, int band, const char *data, ssize_t sz, int packet_ma
 	const char *p = data;
 
 	while (sz) {
-		struct iovec iov[2];
 		unsigned n;
 		char hdr[5];
 
@@ -274,19 +450,12 @@ void send_sideband(int fd, int band, const char *data, ssize_t sz, int packet_ma
 		if (0 <= band) {
 			xsnprintf(hdr, sizeof(hdr), "%04x", n + 5);
 			hdr[4] = band;
-			iov[0].iov_base = hdr;
-			iov[0].iov_len = 5;
+			write_or_die(fd, hdr, 5);
 		} else {
 			xsnprintf(hdr, sizeof(hdr), "%04x", n + 4);
-			iov[0].iov_base = hdr;
-			iov[0].iov_len = 4;
+			write_or_die(fd, hdr, 4);
 		}
-
-		iov[1].iov_base = (void *) p;
-		iov[1].iov_len = n;
-
-		writev_or_die(fd, iov, ARRAY_SIZE(iov));
-
+		write_or_die(fd, p, n);
 		p += n;
 		sz -= n;
 	}
diff --git a/sideband.h b/sideband.h
index 5a25331..d15fa40 100644
--- a/sideband.h
+++ b/sideband.h
@@ -30,4 +30,18 @@ int demultiplex_sideband(const char *me, int status,
 
 void send_sideband(int fd, int band, const char *data, ssize_t sz, int packet_max);
 
+/*
+ * Apply sideband configuration for the given URL. This should be called
+ * when a transport is created to allow URL-specific configuration of
+ * sideband behavior (e.g., sideband.<url>.allowControlCharacters).
+ */
+void sideband_apply_url_config(const char *url);
+
+/*
+ * Parse and set the sideband allow control characters configuration.
+ * The var parameter should be the key name (without section prefix).
+ * Returns 0 if the variable was recognized and handled, non-zero otherwise.
+ */
+int sideband_allow_control_characters_config(const char *var, const char *value);
+
 #endif
diff --git a/src/meson.build b/src/meson.build
index 4573995..41a4b23 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -29,6 +29,7 @@
 )
 libgit_dependencies += declare_dependency(link_with: libgit_rs)
 
+cargo = find_program('cargo', dirs: program_path, native: true, required: get_option('rust'))
 if get_option('tests')
   test('rust', cargo,
     args: [
diff --git a/strbuf.c b/strbuf.c
index 3e04add..8610965 100644
--- a/strbuf.c
+++ b/strbuf.c
@@ -106,12 +106,10 @@ void strbuf_attach(struct strbuf *sb, void *buf, size_t len, size_t alloc)
 void strbuf_grow(struct strbuf *sb, size_t extra)
 {
 	int new_buf = !sb->alloc;
-	if (unsigned_add_overflows(extra, 1) ||
-	    unsigned_add_overflows(sb->len, extra + 1))
-		die("you want to use way too much memory");
+	size_t new_len = st_add3(sb->len, extra, 1);
 	if (new_buf)
 		sb->buf = NULL;
-	ALLOC_GROW(sb->buf, sb->len + extra + 1, sb->alloc);
+	ALLOC_GROW(sb->buf, new_len, sb->alloc);
 	if (new_buf)
 		sb->buf[0] = '\0';
 }
diff --git a/submodule-config.c b/submodule-config.c
index 72a46b7..a81897b 100644
--- a/submodule-config.c
+++ b/submodule-config.c
@@ -1038,5 +1038,5 @@ static int gitmodules_update_clone_config(const char *var, const char *value,
 
 void update_clone_config_from_gitmodules(int *max_jobs)
 {
-	config_from_gitmodules(gitmodules_update_clone_config, the_repository, &max_jobs);
+	config_from_gitmodules(gitmodules_update_clone_config, the_repository, max_jobs);
 }
diff --git a/submodule.c b/submodule.c
index b1a0363..a939ff5 100644
--- a/submodule.c
+++ b/submodule.c
@@ -2620,7 +2620,7 @@ int get_superproject_working_tree(struct strbuf *buf)
 	int code;
 	ssize_t len;
 
-	if (!is_inside_work_tree())
+	if (!is_inside_work_tree(the_repository))
 		/*
 		 * FIXME:
 		 * We might have a superproject, but it is harder
diff --git a/t/helper/meson.build b/t/helper/meson.build
index 675e64c..3235f10 100644
--- a/t/helper/meson.build
+++ b/t/helper/meson.build
@@ -69,6 +69,7 @@
   'test-submodule-nested-repo-config.c',
   'test-submodule.c',
   'test-subprocess.c',
+  'test-synthesize.c',
   'test-tool.c',
   'test-trace2.c',
   'test-truncate.c',
diff --git a/t/helper/test-advise.c b/t/helper/test-advise.c
index 81ed93a..8f9db26 100644
--- a/t/helper/test-advise.c
+++ b/t/helper/test-advise.c
@@ -11,7 +11,7 @@ int cmd__advise_if_enabled(int argc, const char **argv)
 	if (argc != 2)
 		die("usage: %s <advice>", argv[0]);
 
-	setup_git_directory();
+	setup_git_directory(the_repository);
 	repo_config(the_repository, git_default_config, NULL);
 
 	/*
diff --git a/t/helper/test-bitmap.c b/t/helper/test-bitmap.c
index 16a0166..b130832 100644
--- a/t/helper/test-bitmap.c
+++ b/t/helper/test-bitmap.c
@@ -2,7 +2,10 @@
 
 #include "test-tool.h"
 #include "git-compat-util.h"
+#include "hex.h"
+#include "odb.h"
 #include "pack-bitmap.h"
+#include "pseudo-merge.h"
 #include "setup.h"
 
 static int bitmap_list_commits(void)
@@ -35,9 +38,114 @@ static int bitmap_dump_pseudo_merge_objects(uint32_t n)
 	return test_bitmap_pseudo_merge_objects(the_repository, n);
 }
 
+static int add_packed_object(const struct object_id *oid,
+			     struct packed_git *pack,
+			     uint32_t pos,
+			     void *_data)
+{
+	struct packing_data *packed = _data;
+	struct object_entry *entry;
+	struct object_info oi = OBJECT_INFO_INIT;
+	enum object_type type;
+
+	oi.typep = &type;
+
+	entry = packlist_alloc(packed, oid);
+	entry->idx.offset = nth_packed_object_offset(pack, pos);
+	if (packed_object_info(pack, entry->idx.offset, &oi) < 0)
+		die("could not get type of object %s",
+		    oid_to_hex(oid));
+	oe_set_type(entry, type);
+	oe_set_in_pack(packed, entry, pack);
+
+	return 0;
+}
+
+static int idx_oid_cmp(const void *va, const void *vb)
+{
+	const struct pack_idx_entry *a = *(const struct pack_idx_entry **)va;
+	const struct pack_idx_entry *b = *(const struct pack_idx_entry **)vb;
+
+	return oidcmp(&a->oid, &b->oid);
+}
+
+static int bitmap_write(const char *basename)
+{
+	struct packed_git *p = NULL;
+	struct packing_data packed = { 0 };
+	struct bitmap_writer writer;
+	struct pack_idx_entry **index;
+	struct strbuf buf = STRBUF_INIT;
+	uint32_t i;
+
+	prepare_repo_settings(the_repository);
+	repo_for_each_pack(the_repository, p) {
+		if (!strcmp(pack_basename(p), basename))
+			break;
+	}
+
+	if (!p)
+		die("could not find pack '%s'", basename);
+
+	if (open_pack_index(p))
+		die("cannot open pack index for '%s'", p->pack_name);
+
+	prepare_packing_data(the_repository, &packed);
+
+	for_each_object_in_pack(p, add_packed_object, &packed,
+				ODB_FOR_EACH_OBJECT_PACK_ORDER);
+
+	/*
+	 * Build the index array now that data.packed.objects[] is
+	 * fully allocated (packlist_alloc() may have reallocated it
+	 * during the loop above).
+	 */
+	ALLOC_ARRAY(index, p->num_objects);
+	for (i = 0; i < p->num_objects; i++)
+		index[i] = &packed.objects[i].idx;
+
+	bitmap_writer_init(&writer, the_repository, &packed, NULL);
+	bitmap_writer_build_type_index(&writer, index);
+
+	while (strbuf_getline_lf(&buf, stdin) != EOF) {
+		struct object_id oid;
+		struct commit *c;
+
+		if (get_oid_hex(buf.buf, &oid))
+			die("invalid OID: %s", buf.buf);
+
+		c = lookup_commit(the_repository, &oid);
+		if (!c || repo_parse_commit(the_repository, c))
+			die("could not parse commit %s", buf.buf);
+
+		bitmap_writer_push_commit(&writer, c, 0);
+	}
+
+	select_pseudo_merges(&writer);
+	if (bitmap_writer_build(&writer) < 0)
+		die("failed to build bitmaps");
+
+	bitmap_writer_set_checksum(&writer, p->hash);
+
+	QSORT(index, p->num_objects, idx_oid_cmp);
+
+	strbuf_reset(&buf);
+	strbuf_addstr(&buf, p->pack_name);
+	strbuf_strip_suffix(&buf, ".pack");
+	strbuf_addstr(&buf, ".bitmap");
+	bitmap_writer_finish(&writer, index, buf.buf, 0);
+
+	bitmap_writer_free(&writer);
+	strbuf_release(&buf);
+	free(index);
+	clear_packing_data(&packed);
+
+	return 0;
+}
+
 int cmd__bitmap(int argc, const char **argv)
 {
-	setup_git_directory();
+	setup_git_directory(the_repository);
 
 	if (argc == 2 && !strcmp(argv[1], "list-commits"))
 		return bitmap_list_commits();
@@ -51,13 +159,16 @@ int cmd__bitmap(int argc, const char **argv)
 		return bitmap_dump_pseudo_merge_commits(atoi(argv[2]));
 	if (argc == 3 && !strcmp(argv[1], "dump-pseudo-merge-objects"))
 		return bitmap_dump_pseudo_merge_objects(atoi(argv[2]));
+	if (argc == 3 && !strcmp(argv[1], "write"))
+		return bitmap_write(argv[2]);
 
 	usage("\ttest-tool bitmap list-commits\n"
 	      "\ttest-tool bitmap list-commits-with-offset\n"
 	      "\ttest-tool bitmap dump-hashes\n"
 	      "\ttest-tool bitmap dump-pseudo-merges\n"
 	      "\ttest-tool bitmap dump-pseudo-merge-commits <n>\n"
-	      "\ttest-tool bitmap dump-pseudo-merge-objects <n>");
+	      "\ttest-tool bitmap dump-pseudo-merge-objects <n>\n"
+	      "\ttest-tool bitmap write <pack-basename> < <commit-list>");
 
 	return -1;
 }
diff --git a/t/helper/test-bloom.c b/t/helper/test-bloom.c
index 3283544..0c65bef 100644
--- a/t/helper/test-bloom.c
+++ b/t/helper/test-bloom.c
@@ -52,7 +52,7 @@ static const char *const bloom_usage = "\n"
 
 int cmd__bloom(int argc, const char **argv)
 {
-	setup_git_directory();
+	setup_git_directory(the_repository);
 
 	if (argc < 2)
 		usage(bloom_usage);
diff --git a/t/helper/test-cache-tree.c b/t/helper/test-cache-tree.c
index ff61d0c..d42e260 100644
--- a/t/helper/test-cache-tree.c
+++ b/t/helper/test-cache-tree.c
@@ -33,7 +33,7 @@ int cmd__cache_tree(int argc, const char **argv)
 		OPT_END()
 	};
 
-	setup_git_directory();
+	setup_git_directory(the_repository);
 
 	argc = parse_options(argc, argv, NULL, options, test_cache_tree_usage, 0);
 
diff --git a/t/helper/test-config.c b/t/helper/test-config.c
index 9f8cca7..cfb3f4b 100644
--- a/t/helper/test-config.c
+++ b/t/helper/test-config.c
@@ -102,7 +102,7 @@ int cmd__config(int argc, const char **argv)
 		return 0;
 	}
 
-	setup_git_directory();
+	setup_git_directory(the_repository);
 
 	git_configset_init(&cs);
 
diff --git a/t/helper/test-dump-cache-tree.c b/t/helper/test-dump-cache-tree.c
index 3f0c7d0..ccb41a4 100644
--- a/t/helper/test-dump-cache-tree.c
+++ b/t/helper/test-dump-cache-tree.c
@@ -66,7 +66,7 @@ int cmd__dump_cache_tree(int ac UNUSED, const char **av UNUSED)
 	struct cache_tree *another = cache_tree();
 	int ret;
 
-	setup_git_directory();
+	setup_git_directory(the_repository);
 	if (repo_read_index(the_repository) < 0)
 		die("unable to read index file");
 	istate = *the_repository->index;
diff --git a/t/helper/test-dump-fsmonitor.c b/t/helper/test-dump-fsmonitor.c
index efd017c..c991cbb 100644
--- a/t/helper/test-dump-fsmonitor.c
+++ b/t/helper/test-dump-fsmonitor.c
@@ -9,7 +9,7 @@ int cmd__dump_fsmonitor(int ac UNUSED, const char **av UNUSED)
 {
 	struct index_state *istate = the_repository->index;
 
-	setup_git_directory();
+	setup_git_directory(the_repository);
 	if (do_read_index(istate, the_repository->index_file, 0) < 0)
 		die("unable to read index file");
 	if (!istate->fsmonitor_last_update) {
diff --git a/t/helper/test-dump-split-index.c b/t/helper/test-dump-split-index.c
index f855a38..aae0a40 100644
--- a/t/helper/test-dump-split-index.c
+++ b/t/helper/test-dump-split-index.c
@@ -17,7 +17,7 @@ int cmd__dump_split_index(int ac UNUSED, const char **av)
 {
 	struct split_index *si;
 
-	setup_git_directory();
+	setup_git_directory(the_repository);
 
 	do_read_index(the_repository->index, av[1], 1);
 	printf("own %s\n", oid_to_hex(&the_repository->index->oid));
diff --git a/t/helper/test-dump-untracked-cache.c b/t/helper/test-dump-untracked-cache.c
index 01a1094..24308bd 100644
--- a/t/helper/test-dump-untracked-cache.c
+++ b/t/helper/test-dump-untracked-cache.c
@@ -54,7 +54,7 @@ int cmd__dump_untracked_cache(int ac UNUSED, const char **av UNUSED)
 	xsetenv("GIT_CONFIG_KEY_0", "core.untrackedCache", 1);
 	xsetenv("GIT_CONFIG_VALUE_0", "keep", 1);
 
-	setup_git_directory();
+	setup_git_directory(the_repository);
 	if (repo_read_index(the_repository) < 0)
 		die("unable to read index file");
 	uc = the_repository->index->untracked;
diff --git a/t/helper/test-find-pack.c b/t/helper/test-find-pack.c
index fc4b8a7..28d5b1f 100644
--- a/t/helper/test-find-pack.c
+++ b/t/helper/test-find-pack.c
@@ -25,7 +25,7 @@ int cmd__find_pack(int argc, const char **argv)
 	struct object_id oid;
 	struct packed_git *p;
 	int count = -1, actual_count = 0;
-	const char *prefix = setup_git_directory();
+	const char *prefix = setup_git_directory(the_repository);
 
 	struct option options[] = {
 		OPT_INTEGER('c', "check-count", &count, "expected number of packs"),
diff --git a/t/helper/test-fsmonitor-client.c b/t/helper/test-fsmonitor-client.c
index 02bfe92..dc1dff2 100644
--- a/t/helper/test-fsmonitor-client.c
+++ b/t/helper/test-fsmonitor-client.c
@@ -210,7 +210,7 @@ int cmd__fsmonitor_client(int argc, const char **argv)
 
 	subcmd = argv[0];
 
-	setup_git_directory();
+	setup_git_directory(the_repository);
 
 	if (!strcmp(subcmd, "query"))
 		return !!do_send_query(token);
diff --git a/t/helper/test-lazy-init-name-hash.c b/t/helper/test-lazy-init-name-hash.c
index 40f5df4..e542985 100644
--- a/t/helper/test-lazy-init-name-hash.c
+++ b/t/helper/test-lazy-init-name-hash.c
@@ -211,7 +211,7 @@ int cmd__lazy_init_name_hash(int argc, const char **argv)
 	const char *prefix;
 	uint64_t avg_single, avg_multi;
 
-	prefix = setup_git_directory();
+	prefix = setup_git_directory(the_repository);
 
 	argc = parse_options(argc, argv, prefix, options, usage, 0);
 
diff --git a/t/helper/test-match-trees.c b/t/helper/test-match-trees.c
index 2ed064b..006ce52 100644
--- a/t/helper/test-match-trees.c
+++ b/t/helper/test-match-trees.c
@@ -13,7 +13,7 @@ int cmd__match_trees(int ac UNUSED, const char **av)
 	struct object_id hash1, hash2, shifted;
 	struct tree *one, *two;
 
-	setup_git_directory();
+	setup_git_directory(the_repository);
 
 	if (repo_get_oid(the_repository, av[1], &hash1))
 		die("cannot parse %s as an object name", av[1]);
diff --git a/t/helper/test-pack-deltas.c b/t/helper/test-pack-deltas.c
index 4981401..c493b75 100644
--- a/t/helper/test-pack-deltas.c
+++ b/t/helper/test-pack-deltas.c
@@ -95,7 +95,7 @@ int cmd__pack_deltas(int argc, const char **argv)
 	if (argc || num_objects < 0)
 		usage_with_options(usage_str, options);
 
-	setup_git_directory();
+	setup_git_directory(the_repository);
 
 	f = hashfd(the_repository->hash_algo, 1, "<stdout>");
 	write_pack_header(f, num_objects);
diff --git a/t/helper/test-pack-mtimes.c b/t/helper/test-pack-mtimes.c
index 7a8ee1d..b774056 100644
--- a/t/helper/test-pack-mtimes.c
+++ b/t/helper/test-pack-mtimes.c
@@ -32,7 +32,7 @@ int cmd__pack_mtimes(int argc, const char **argv)
 	struct strbuf buf = STRBUF_INIT;
 	struct packed_git *p;
 
-	setup_git_directory();
+	setup_git_directory(the_repository);
 
 	if (argc != 2)
 		usage(pack_mtimes_usage);
diff --git a/t/helper/test-partial-clone.c b/t/helper/test-partial-clone.c
index d848800..a7aab42 100644
--- a/t/helper/test-partial-clone.c
+++ b/t/helper/test-partial-clone.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
 #include "test-tool.h"
 #include "hex.h"
 #include "repository.h"
@@ -32,7 +34,7 @@ static void object_info(const char *gitdir, const char *oid_hex)
 
 int cmd__partial_clone(int argc, const char **argv)
 {
-	setup_git_directory();
+	setup_git_directory(the_repository);
 
 	if (argc < 4)
 		die("too few arguments");
diff --git a/t/helper/test-path-utils.c b/t/helper/test-path-utils.c
index 874542e..15eb444 100644
--- a/t/helper/test-path-utils.c
+++ b/t/helper/test-path-utils.c
@@ -377,9 +377,9 @@ int cmd__path_utils(int argc, const char **argv)
 		const char *prefix = argv[2];
 		int prefix_len = strlen(prefix);
 		int nongit_ok;
-		setup_git_directory_gently(&nongit_ok);
+		setup_git_directory_gently(the_repository, &nongit_ok);
 		while (argc > 3) {
-			char *pfx = prefix_path(prefix, prefix_len, argv[3]);
+			char *pfx = prefix_path(the_repository, prefix, prefix_len, argv[3]);
 
 			puts(pfx);
 			free(pfx);
diff --git a/t/helper/test-path-walk.c b/t/helper/test-path-walk.c
index fe63002..4233bad 100644
--- a/t/helper/test-path-walk.c
+++ b/t/helper/test-path-walk.c
@@ -4,6 +4,7 @@
 #include "dir.h"
 #include "environment.h"
 #include "hex.h"
+#include "list-objects-filter-options.h"
 #include "object-name.h"
 #include "object.h"
 #include "pretty.h"
@@ -67,10 +68,12 @@ static int emit_block(const char *path, struct oid_array *oids,
 
 int cmd__path_walk(int argc, const char **argv)
 {
-	int res, stdin_pl = 0;
+	int res, stdin_pl = 0, pl_sparse_trees = -1;
 	struct rev_info revs = REV_INFO_INIT;
 	struct path_walk_info info = PATH_WALK_INFO_INIT;
 	struct path_walk_test_data data = { 0 };
+	struct list_objects_filter_options filter_options =
+		LIST_OBJECTS_FILTER_INIT;
 	struct option options[] = {
 		OPT_BOOL(0, "blobs", &info.blobs,
 			 N_("toggle inclusion of blob objects")),
@@ -86,11 +89,14 @@ int cmd__path_walk(int argc, const char **argv)
 			 N_("toggle aggressive edge walk")),
 		OPT_BOOL(0, "stdin-pl", &stdin_pl,
 			 N_("read a pattern list over stdin")),
+		OPT_BOOL(0, "pl-sparse-trees", &pl_sparse_trees,
+			 N_("toggle pruning of trees by sparse patterns")),
+		OPT_PARSE_LIST_OBJECTS_FILTER(&filter_options),
 		OPT_END(),
 	};
 
-	setup_git_directory();
-	revs.repo = the_repository;
+	setup_git_directory(the_repository);
+	repo_init_revisions(the_repository, &revs, NULL);
 
 	argc = parse_options(argc, argv, NULL,
 			     options, path_walk_usage,
@@ -101,6 +107,10 @@ int cmd__path_walk(int argc, const char **argv)
 	else
 		usage(path_walk_usage[0]);
 
+	/* Apply the filter after setup_revisions to avoid the --objects check. */
+	if (filter_options.choice)
+		list_objects_filter_copy(&revs.filter, &filter_options);
+
 	info.revs = &revs;
 	info.path_fn = emit_block;
 	info.path_fn_data = &data;
@@ -108,6 +118,8 @@ int cmd__path_walk(int argc, const char **argv)
 	if (stdin_pl) {
 		struct strbuf in = STRBUF_INIT;
 		CALLOC_ARRAY(info.pl, 1);
+		info.pl_sparse_trees = (pl_sparse_trees >= 0) ?
+			pl_sparse_trees : 1;
 
 		info.pl->use_cone_patterns = 1;
 
@@ -129,6 +141,7 @@ int cmd__path_walk(int argc, const char **argv)
 		free(info.pl);
 	}
 
+	list_objects_filter_release(&filter_options);
 	release_revisions(&revs);
 	return res;
 }
diff --git a/t/helper/test-reach.c b/t/helper/test-reach.c
index 3131b54..5d86a96 100644
--- a/t/helper/test-reach.c
+++ b/t/helper/test-reach.c
@@ -39,7 +39,7 @@ int cmd__reach(int ac, const char **av)
 	struct strbuf buf = STRBUF_INIT;
 	struct repository *r = the_repository;
 
-	setup_git_directory();
+	setup_git_directory(the_repository);
 
 	if (ac < 2)
 		exit(1);
diff --git a/t/helper/test-read-cache.c b/t/helper/test-read-cache.c
index 9ae71ce..6b08ba8 100644
--- a/t/helper/test-read-cache.c
+++ b/t/helper/test-read-cache.c
@@ -19,7 +19,7 @@ int cmd__read_cache(int argc, const char **argv)
 
 	if (argc == 2)
 		cnt = strtol(argv[1], NULL, 0);
-	setup_git_directory();
+	setup_git_directory(the_repository);
 	repo_config(the_repository, git_default_config, NULL);
 
 	for (i = 0; i < cnt; i++) {
diff --git a/t/helper/test-read-graph.c b/t/helper/test-read-graph.c
index 6a5f64e..9f07b9c 100644
--- a/t/helper/test-read-graph.c
+++ b/t/helper/test-read-graph.c
@@ -76,7 +76,7 @@ int cmd__read_graph(int argc, const char **argv)
 	struct odb_source *source;
 	int ret = 0;
 
-	setup_git_directory();
+	setup_git_directory(the_repository);
 	source = the_repository->objects->sources;
 
 	prepare_repo_settings(the_repository);
diff --git a/t/helper/test-read-midx.c b/t/helper/test-read-midx.c
index 388d29e..790000f 100644
--- a/t/helper/test-read-midx.c
+++ b/t/helper/test-read-midx.c
@@ -14,7 +14,7 @@
 static struct multi_pack_index *setup_midx(const char *object_dir)
 {
 	struct odb_source *source;
-	setup_git_directory();
+	setup_git_directory(the_repository);
 	source = odb_find_source(the_repository->objects, object_dir);
 	if (!source)
 		source = odb_add_to_alternates_memory(the_repository->objects,
diff --git a/t/helper/test-ref-store.c b/t/helper/test-ref-store.c
index 74edf20..3866d0a 100644
--- a/t/helper/test-ref-store.c
+++ b/t/helper/test-ref-store.c
@@ -340,7 +340,7 @@ int cmd__ref_store(int argc UNUSED, const char **argv)
 	const char *func;
 	struct command *cmd;
 
-	setup_git_directory();
+	setup_git_directory(the_repository);
 
 	argv = get_store(argv + 1, &refs);
 
diff --git a/t/helper/test-revision-walking.c b/t/helper/test-revision-walking.c
index 071f5bd..70051ee 100644
--- a/t/helper/test-revision-walking.c
+++ b/t/helper/test-revision-walking.c
@@ -56,7 +56,7 @@ int cmd__revision_walking(int argc, const char **argv)
 	if (argc < 2)
 		return 1;
 
-	setup_git_directory();
+	setup_git_directory(the_repository);
 
 	if (!strcmp(argv[1], "run-twice")) {
 		printf("1st\n");
diff --git a/t/helper/test-scrap-cache-tree.c b/t/helper/test-scrap-cache-tree.c
index 64fff6e..7b5ce50 100644
--- a/t/helper/test-scrap-cache-tree.c
+++ b/t/helper/test-scrap-cache-tree.c
@@ -12,7 +12,7 @@ int cmd__scrap_cache_tree(int ac UNUSED, const char **av UNUSED)
 {
 	struct lock_file index_lock = LOCK_INIT;
 
-	setup_git_directory();
+	setup_git_directory(the_repository);
 	repo_hold_locked_index(the_repository, &index_lock, LOCK_DIE_ON_ERROR);
 	if (repo_read_index(the_repository) < 0)
 		die("unable to read index file");
diff --git a/t/helper/test-serve-v2.c b/t/helper/test-serve-v2.c
index 63a200b..27f3ed8 100644
--- a/t/helper/test-serve-v2.c
+++ b/t/helper/test-serve-v2.c
@@ -23,7 +23,7 @@ int cmd__serve_v2(int argc, const char **argv)
 			 N_("exit immediately after advertising capabilities")),
 		OPT_END()
 	};
-	const char *prefix = setup_git_directory();
+	const char *prefix = setup_git_directory(the_repository);
 
 	/* ignore all unknown cmdline switches for now */
 	argc = parse_options(argc, argv, prefix, options, serve_usage,
diff --git a/t/helper/test-submodule-config.c b/t/helper/test-submodule-config.c
index cbe93f2..3f30292 100644
--- a/t/helper/test-submodule-config.c
+++ b/t/helper/test-submodule-config.c
@@ -34,7 +34,7 @@ int cmd__submodule_config(int argc, const char **argv)
 	if (my_argc % 2 != 0)
 		die_usage(argc, argv, "Wrong number of arguments.");
 
-	setup_git_directory();
+	setup_git_directory(the_repository);
 
 	while (*arg) {
 		struct object_id commit_oid;
diff --git a/t/helper/test-submodule-nested-repo-config.c b/t/helper/test-submodule-nested-repo-config.c
index 2710341..7e31d3f 100644
--- a/t/helper/test-submodule-nested-repo-config.c
+++ b/t/helper/test-submodule-nested-repo-config.c
@@ -19,7 +19,7 @@ int cmd__submodule_nested_repo_config(int argc, const char **argv)
 	if (argc < 3)
 		die_usage(argv, "Wrong number of arguments.");
 
-	setup_git_directory();
+	setup_git_directory(the_repository);
 
 	if (repo_submodule_init(&subrepo, the_repository, argv[1], null_oid(the_hash_algo))) {
 		die_usage(argv, "Submodule not found.");
diff --git a/t/helper/test-submodule.c b/t/helper/test-submodule.c
index 0133852..3c5c4c4 100644
--- a/t/helper/test-submodule.c
+++ b/t/helper/test-submodule.c
@@ -99,7 +99,7 @@ static int cmd__submodule_is_active(int argc, const char **argv)
 	if (argc != 1)
 		usage_with_options(submodule_is_active_usage, options);
 
-	setup_git_directory();
+	setup_git_directory(the_repository);
 
 	return !is_submodule_active(the_repository, argv[0]);
 }
@@ -142,7 +142,7 @@ static int cmd__submodule_config_list(int argc, const char **argv)
 	argc = parse_options(argc, argv, "test-tools", options, usage,
 			     PARSE_OPT_KEEP_ARGV0);
 
-	setup_git_directory();
+	setup_git_directory(the_repository);
 
 	if (argc == 2)
 		return print_config_from_gitmodules(the_repository, argv[1]);
@@ -161,7 +161,7 @@ static int cmd__submodule_config_set(int argc, const char **argv)
 	argc = parse_options(argc, argv, "test-tools", options, usage,
 			     PARSE_OPT_KEEP_ARGV0);
 
-	setup_git_directory();
+	setup_git_directory(the_repository);
 
 	/* Equivalent to ACTION_SET in builtin/config.c */
 	if (argc == 3) {
@@ -183,7 +183,7 @@ static int cmd__submodule_config_unset(int argc, const char **argv)
 		NULL
 	};
 
-	setup_git_directory();
+	setup_git_directory(the_repository);
 
 	if (argc == 2) {
 		if (!is_writing_gitmodules_ok())
@@ -202,7 +202,7 @@ static int cmd__submodule_config_writeable(int argc, const char **argv UNUSED)
 		"test-tool submodule config-writeable",
 		NULL
 	};
-	setup_git_directory();
+	setup_git_directory(the_repository);
 
 	if (argc == 1)
 		return is_writing_gitmodules_ok() ? 0 : -1;
diff --git a/t/helper/test-subprocess.c b/t/helper/test-subprocess.c
index c344f16..a8194d2 100644
--- a/t/helper/test-subprocess.c
+++ b/t/helper/test-subprocess.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
 #include "test-tool.h"
 #include "run-command.h"
 #include "setup.h"
@@ -7,11 +9,11 @@ int cmd__subprocess(int argc, const char **argv)
 	struct child_process cp = CHILD_PROCESS_INIT;
 	int nogit = 0;
 
-	setup_git_directory_gently(&nogit);
+	setup_git_directory_gently(the_repository, &nogit);
 	if (nogit)
 		die("No git repo found");
 	if (argc > 1 && !strcmp(argv[1], "--setup-work-tree")) {
-		setup_work_tree();
+		setup_work_tree(the_repository);
 		argv++;
 	}
 	cp.git_cmd = 1;
diff --git a/t/helper/test-synthesize.c b/t/helper/test-synthesize.c
new file mode 100644
index 0000000..3fa534f
--- /dev/null
+++ b/t/helper/test-synthesize.c
@@ -0,0 +1,541 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
+#include "test-tool.h"
+#include "git-compat-util.h"
+#include "git-zlib.h"
+#include "hash.h"
+#include "hex.h"
+#include "object-file.h"
+#include "object.h"
+#include "pack.h"
+#include "parse-options.h"
+#include "parse.h"
+#include "repository.h"
+#include "setup.h"
+#include "strbuf.h"
+#include "write-or-die.h"
+
+#define BLOCK_SIZE 0xffff
+static const unsigned char zeros[BLOCK_SIZE];
+
+/*
+ * Write data as an uncompressed zlib stream.
+ * For data larger than 64KB, writes multiple uncompressed blocks.
+ * If data is NULL, writes zeros.
+ * Updates the pack checksum context.
+ */
+static void write_uncompressed_zlib(FILE *f, struct git_hash_ctx *pack_ctx,
+				    const void *data, size_t len,
+				    const struct git_hash_algo *algo)
+{
+	unsigned char zlib_header[2] = { 0x78, 0x01 }; /* CMF, FLG */
+	unsigned char block_header[5];
+	const unsigned char *p = data;
+	size_t remaining = len;
+	uint32_t adler = 1L; /* adler32 initial value */
+	unsigned char adler_buf[4];
+
+	/* Write zlib header */
+	fwrite_or_die(f, zlib_header, sizeof(zlib_header));
+	algo->update_fn(pack_ctx, zlib_header, 2);
+
+	/* Write uncompressed blocks (max 64KB each) */
+	do {
+		size_t block_len = remaining > BLOCK_SIZE ? BLOCK_SIZE : remaining;
+		int is_final = (block_len == remaining);
+		const unsigned char *block_data = data ? p : zeros;
+
+		block_header[0] = is_final ? 0x01 : 0x00;
+		block_header[1] = block_len & 0xff;
+		block_header[2] = (block_len >> 8) & 0xff;
+		block_header[3] = block_header[1] ^ 0xff;
+		block_header[4] = block_header[2] ^ 0xff;
+
+		fwrite_or_die(f, block_header, sizeof(block_header));
+		algo->update_fn(pack_ctx, block_header, 5);
+
+		if (block_len) {
+			fwrite_or_die(f, block_data, block_len);
+			algo->update_fn(pack_ctx, block_data, block_len);
+			adler = adler32(adler, block_data, block_len);
+		}
+
+		if (data)
+			p += block_len;
+		remaining -= block_len;
+	} while (remaining > 0);
+
+	/* Write adler32 checksum */
+	put_be32(adler_buf, adler);
+	fwrite_or_die(f, adler_buf, sizeof(adler_buf));
+	algo->update_fn(pack_ctx, adler_buf, 4);
+}
+
+/*
+ * Write an uncompressed object to the pack file.
+ * If `data == NULL`, it is treated like a buffer to NUL bytes.
+ * Updates the pack checksum context.
+ */
+static void write_pack_object(FILE *f, struct git_hash_ctx *pack_ctx,
+			      enum object_type type,
+			      const void *data, size_t len,
+			      struct object_id *oid,
+			      const struct git_hash_algo *algo)
+{
+	unsigned char pack_header[MAX_PACK_OBJECT_HEADER];
+	char object_header[32];
+	int pack_header_len, object_header_len;
+	struct git_hash_ctx ctx;
+
+	/* Write pack object header */
+	pack_header_len = encode_in_pack_object_header(pack_header,
+						       sizeof(pack_header),
+						       type, len);
+	fwrite_or_die(f, pack_header, pack_header_len);
+	algo->update_fn(pack_ctx, pack_header, pack_header_len);
+
+	/* Write the data as uncompressed zlib */
+	write_uncompressed_zlib(f, pack_ctx, data, len, algo);
+
+	algo->init_fn(&ctx);
+	object_header_len = format_object_header(object_header,
+						 sizeof(object_header),
+						 type, len);
+	algo->update_fn(&ctx, object_header, object_header_len);
+	if (data)
+		algo->update_fn(&ctx, data, len);
+	else {
+		for (size_t i = len / BLOCK_SIZE; i; i--)
+			algo->update_fn(&ctx, zeros, BLOCK_SIZE);
+		algo->update_fn(&ctx, zeros, len % BLOCK_SIZE);
+	}
+	algo->final_oid_fn(oid, &ctx);
+}
+
+/*
+ * Fast path: precomputed pack data for a 4 GiB + 1 all-NUL blob.
+ *
+ * The generated pack is almost entirely zeros with a small constant
+ * prefix, periodic deflate block headers, and a constant suffix
+ * containing the tree, two commits, and the pack checksum.  Because
+ * every byte is deterministic for a given blob size and hash algorithm,
+ * we can write the pack without computing any hashes at all, reducing
+ * runtime from minutes of hash computation to seconds of pure I/O.
+ *
+ * The blob is stored as an uncompressed deflate stream: a two-byte
+ * zlib header, then 65538 blocks of up to 0xffff bytes each, followed
+ * by an adler32 checksum.  The pack header and deflate framing are
+ * shared across hash algorithms; only the suffix (which contains OIDs
+ * and the pack checksum) differs.
+ *
+ * Constants were generated by running the generic path and extracting
+ * the non-zero bytes from the resulting pack file.
+ */
+
+#define FAST_PACK_4G1_BLOB_SIZE ((size_t)4 * 1024 * 1024 * 1024 + 1)
+#define FAST_PACK_4G1_N_FULL_BLOCKS 65537
+
+/*
+ * Per-hash-algorithm constants for the fast path.  The prefix and
+ * deflate block structure are identical across algorithms; only the
+ * suffix (tree, commits, pack checksum) and the commit OID differ.
+ */
+struct fast_pack {
+	uint32_t format_id;
+	const unsigned char *suffix;
+	size_t suffix_len;
+	const char *commit_oid;
+};
+
+/* Pack header + pack object header + zlib header + first block header */
+static const unsigned char fast_pack_prefix[] = {
+	/* PACK header: signature, version 2, 5 objects */
+	0x50, 0x41, 0x43, 0x4b, 0x00, 0x00, 0x00, 0x02,
+	0x00, 0x00, 0x00, 0x05,
+	/* pack object header: blob, size = 4294967297 */
+	0xb1, 0x80, 0x80, 0x80, 0x80, 0x01,
+	/* zlib header: CMF=0x78, FLG=0x01 */
+	0x78, 0x01,
+	/* first non-final block header: BFINAL=0, LEN=0xffff, NLEN=0x0000 */
+	0x00, 0xff, 0xff, 0x00, 0x00
+};
+
+/* Every non-final deflate block header is identical */
+static const unsigned char fast_pack_block_header[] = {
+	0x00, 0xff, 0xff, 0x00, 0x00
+};
+
+/* Final block (2 data bytes) + adler32 of 4294967297 NUL bytes */
+static const unsigned char fast_pack_final_block[] = {
+	/* BFINAL=1, LEN=2, NLEN=0xfffd */
+	0x01, 0x02, 0x00, 0xfd, 0xff,
+	/* 2 NUL data bytes */
+	0x00, 0x00,
+	/* adler32 */
+	0x00, 0xe2, 0x00, 0x01
+};
+
+/*
+ * SHA-1 suffix: tree, commit, empty tree, final commit, pack checksum.
+ */
+static const unsigned char fast_pack_sha1_suffix[] = {
+	0xa0, 0x02, 0x78, 0x01, 0x01, 0x20, 0x00, 0xdf,
+	0xff, 0x31, 0x30, 0x30, 0x36, 0x34, 0x34, 0x20,
+	0x66, 0x69, 0x6c, 0x65, 0x00, 0x3e, 0xb7, 0xfe,
+	0xb1, 0x41, 0x3c, 0x75, 0x7f, 0x0d, 0x81, 0x81,
+	0xde, 0xb2, 0x8d, 0x1d, 0xab, 0x03, 0xd6, 0x48,
+	0x46, 0xb4, 0xb4, 0x0c, 0x60, 0x95, 0x0b, 0x78,
+	0x01, 0x01, 0xb5, 0x00, 0x4a, 0xff, 0x74, 0x72,
+	0x65, 0x65, 0x20, 0x63, 0x36, 0x38, 0x33, 0x66,
+	0x63, 0x63, 0x37, 0x64, 0x31, 0x64, 0x38, 0x33,
+	0x65, 0x66, 0x32, 0x66, 0x65, 0x31, 0x61, 0x66,
+	0x35, 0x35, 0x32, 0x31, 0x35, 0x64, 0x30, 0x31,
+	0x36, 0x38, 0x64, 0x62, 0x35, 0x32, 0x61, 0x33,
+	0x61, 0x33, 0x62, 0x0a, 0x61, 0x75, 0x74, 0x68,
+	0x6f, 0x72, 0x20, 0x41, 0x20, 0x55, 0x20, 0x54,
+	0x68, 0x6f, 0x72, 0x20, 0x3c, 0x61, 0x75, 0x74,
+	0x68, 0x6f, 0x72, 0x40, 0x65, 0x78, 0x61, 0x6d,
+	0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x3e,
+	0x20, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
+	0x38, 0x39, 0x30, 0x20, 0x2b, 0x30, 0x30, 0x30,
+	0x30, 0x0a, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74,
+	0x74, 0x65, 0x72, 0x20, 0x43, 0x20, 0x4f, 0x20,
+	0x4d, 0x69, 0x74, 0x74, 0x65, 0x72, 0x20, 0x3c,
+	0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x74, 0x65,
+	0x72, 0x40, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c,
+	0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x3e, 0x20, 0x31,
+	0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
+	0x30, 0x20, 0x2b, 0x30, 0x30, 0x30, 0x30, 0x0a,
+	0x0a, 0x4c, 0x61, 0x72, 0x67, 0x65, 0x20, 0x62,
+	0x6c, 0x6f, 0x62, 0x20, 0x63, 0x6f, 0x6d, 0x6d,
+	0x69, 0x74, 0x0a, 0xc6, 0x55, 0x37, 0x6b, 0x20,
+	0x78, 0x01, 0x01, 0x00, 0x00, 0xff, 0xff, 0x00,
+	0x00, 0x00, 0x01, 0x95, 0x0e, 0x78, 0x01, 0x01,
+	0xe5, 0x00, 0x1a, 0xff, 0x74, 0x72, 0x65, 0x65,
+	0x20, 0x34, 0x62, 0x38, 0x32, 0x35, 0x64, 0x63,
+	0x36, 0x34, 0x32, 0x63, 0x62, 0x36, 0x65, 0x62,
+	0x39, 0x61, 0x30, 0x36, 0x30, 0x65, 0x35, 0x34,
+	0x62, 0x66, 0x38, 0x64, 0x36, 0x39, 0x32, 0x38,
+	0x38, 0x66, 0x62, 0x65, 0x65, 0x34, 0x39, 0x30,
+	0x34, 0x0a, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74,
+	0x20, 0x63, 0x35, 0x62, 0x32, 0x31, 0x63, 0x36,
+	0x31, 0x31, 0x61, 0x61, 0x35, 0x39, 0x34, 0x65,
+	0x63, 0x39, 0x66, 0x64, 0x37, 0x65, 0x39, 0x32,
+	0x63, 0x66, 0x39, 0x36, 0x34, 0x38, 0x39, 0x31,
+	0x34, 0x63, 0x61, 0x34, 0x63, 0x32, 0x34, 0x31,
+	0x32, 0x0a, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72,
+	0x20, 0x41, 0x20, 0x55, 0x20, 0x54, 0x68, 0x6f,
+	0x72, 0x20, 0x3c, 0x61, 0x75, 0x74, 0x68, 0x6f,
+	0x72, 0x40, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c,
+	0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x3e, 0x20, 0x31,
+	0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
+	0x30, 0x20, 0x2b, 0x30, 0x30, 0x30, 0x30, 0x0a,
+	0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x74, 0x65,
+	0x72, 0x20, 0x43, 0x20, 0x4f, 0x20, 0x4d, 0x69,
+	0x74, 0x74, 0x65, 0x72, 0x20, 0x3c, 0x63, 0x6f,
+	0x6d, 0x6d, 0x69, 0x74, 0x74, 0x65, 0x72, 0x40,
+	0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e,
+	0x63, 0x6f, 0x6d, 0x3e, 0x20, 0x31, 0x32, 0x33,
+	0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x30, 0x20,
+	0x2b, 0x30, 0x30, 0x30, 0x30, 0x0a, 0x0a, 0x45,
+	0x6d, 0x70, 0x74, 0x79, 0x20, 0x74, 0x72, 0x65,
+	0x65, 0x20, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74,
+	0x0a, 0xaa, 0xb8, 0x45, 0x01, 0x8e, 0xfc, 0xf0,
+	0x2f, 0x9c, 0xc5, 0xcc, 0x4f, 0x6a, 0x1a, 0xc9,
+	0x2b, 0x23, 0xa9, 0xff, 0x91, 0x06, 0xc2, 0x70,
+	0xe3
+};
+
+/*
+ * SHA-256 suffix: same structure, but with 32-byte OIDs and SHA-256
+ * pack checksum (609 bytes vs 513 for SHA-1).
+ */
+static const unsigned char fast_pack_sha256_suffix[] = {
+	0xac, 0x02, 0x78, 0x01, 0x01, 0x2c, 0x00, 0xd3,
+	0xff, 0x31, 0x30, 0x30, 0x36, 0x34, 0x34, 0x20,
+	0x66, 0x69, 0x6c, 0x65, 0x00, 0x42, 0x53, 0xc1,
+	0x8a, 0x9f, 0x5e, 0xc3, 0xbb, 0x47, 0xb0, 0x83,
+	0x8a, 0x19, 0xdb, 0x31, 0xbb, 0x7b, 0x0f, 0x3b,
+	0x80, 0xa4, 0xbc, 0x2f, 0xaf, 0x72, 0x6b, 0xdb,
+	0x62, 0xaa, 0xba, 0xdd, 0xde, 0x77, 0xc6, 0x13,
+	0xeb, 0x9d, 0x0c, 0x78, 0x01, 0x01, 0xcd, 0x00,
+	0x32, 0xff, 0x74, 0x72, 0x65, 0x65, 0x20, 0x62,
+	0x36, 0x30, 0x39, 0x37, 0x37, 0x64, 0x37, 0x63,
+	0x34, 0x63, 0x32, 0x64, 0x31, 0x65, 0x63, 0x63,
+	0x33, 0x66, 0x62, 0x61, 0x31, 0x64, 0x39, 0x38,
+	0x65, 0x65, 0x31, 0x32, 0x30, 0x61, 0x64, 0x63,
+	0x32, 0x34, 0x38, 0x33, 0x34, 0x39, 0x35, 0x30,
+	0x62, 0x65, 0x34, 0x31, 0x32, 0x64, 0x39, 0x34,
+	0x63, 0x38, 0x30, 0x39, 0x34, 0x38, 0x30, 0x66,
+	0x35, 0x38, 0x62, 0x61, 0x39, 0x64, 0x61, 0x0a,
+	0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x20, 0x41,
+	0x20, 0x55, 0x20, 0x54, 0x68, 0x6f, 0x72, 0x20,
+	0x3c, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x40,
+	0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e,
+	0x63, 0x6f, 0x6d, 0x3e, 0x20, 0x31, 0x32, 0x33,
+	0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x30, 0x20,
+	0x2b, 0x30, 0x30, 0x30, 0x30, 0x0a, 0x63, 0x6f,
+	0x6d, 0x6d, 0x69, 0x74, 0x74, 0x65, 0x72, 0x20,
+	0x43, 0x20, 0x4f, 0x20, 0x4d, 0x69, 0x74, 0x74,
+	0x65, 0x72, 0x20, 0x3c, 0x63, 0x6f, 0x6d, 0x6d,
+	0x69, 0x74, 0x74, 0x65, 0x72, 0x40, 0x65, 0x78,
+	0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f,
+	0x6d, 0x3e, 0x20, 0x31, 0x32, 0x33, 0x34, 0x35,
+	0x36, 0x37, 0x38, 0x39, 0x30, 0x20, 0x2b, 0x30,
+	0x30, 0x30, 0x30, 0x0a, 0x0a, 0x4c, 0x61, 0x72,
+	0x67, 0x65, 0x20, 0x62, 0x6c, 0x6f, 0x62, 0x20,
+	0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x0a, 0xb7,
+	0x80, 0x3d, 0xd7, 0x20, 0x78, 0x01, 0x01, 0x00,
+	0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x95,
+	0x11, 0x78, 0x01, 0x01, 0x15, 0x01, 0xea, 0xfe,
+	0x74, 0x72, 0x65, 0x65, 0x20, 0x36, 0x65, 0x66,
+	0x31, 0x39, 0x62, 0x34, 0x31, 0x32, 0x32, 0x35,
+	0x63, 0x35, 0x33, 0x36, 0x39, 0x66, 0x31, 0x63,
+	0x31, 0x30, 0x34, 0x64, 0x34, 0x35, 0x64, 0x38,
+	0x64, 0x38, 0x35, 0x65, 0x66, 0x61, 0x39, 0x62,
+	0x30, 0x35, 0x37, 0x62, 0x35, 0x33, 0x62, 0x31,
+	0x34, 0x62, 0x34, 0x62, 0x39, 0x62, 0x39, 0x33,
+	0x39, 0x64, 0x64, 0x37, 0x34, 0x64, 0x65, 0x63,
+	0x63, 0x35, 0x33, 0x32, 0x31, 0x0a, 0x70, 0x61,
+	0x72, 0x65, 0x6e, 0x74, 0x20, 0x37, 0x35, 0x62,
+	0x66, 0x30, 0x63, 0x34, 0x37, 0x61, 0x65, 0x34,
+	0x62, 0x62, 0x33, 0x30, 0x38, 0x65, 0x37, 0x63,
+	0x63, 0x32, 0x34, 0x38, 0x32, 0x65, 0x32, 0x32,
+	0x65, 0x66, 0x61, 0x65, 0x33, 0x37, 0x38, 0x37,
+	0x61, 0x39, 0x36, 0x38, 0x34, 0x38, 0x62, 0x64,
+	0x31, 0x37, 0x34, 0x39, 0x35, 0x36, 0x37, 0x31,
+	0x34, 0x37, 0x31, 0x35, 0x32, 0x34, 0x36, 0x64,
+	0x64, 0x62, 0x64, 0x35, 0x34, 0x0a, 0x61, 0x75,
+	0x74, 0x68, 0x6f, 0x72, 0x20, 0x41, 0x20, 0x55,
+	0x20, 0x54, 0x68, 0x6f, 0x72, 0x20, 0x3c, 0x61,
+	0x75, 0x74, 0x68, 0x6f, 0x72, 0x40, 0x65, 0x78,
+	0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f,
+	0x6d, 0x3e, 0x20, 0x31, 0x32, 0x33, 0x34, 0x35,
+	0x36, 0x37, 0x38, 0x39, 0x30, 0x20, 0x2b, 0x30,
+	0x30, 0x30, 0x30, 0x0a, 0x63, 0x6f, 0x6d, 0x6d,
+	0x69, 0x74, 0x74, 0x65, 0x72, 0x20, 0x43, 0x20,
+	0x4f, 0x20, 0x4d, 0x69, 0x74, 0x74, 0x65, 0x72,
+	0x20, 0x3c, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74,
+	0x74, 0x65, 0x72, 0x40, 0x65, 0x78, 0x61, 0x6d,
+	0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x3e,
+	0x20, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
+	0x38, 0x39, 0x30, 0x20, 0x2b, 0x30, 0x30, 0x30,
+	0x30, 0x0a, 0x0a, 0x45, 0x6d, 0x70, 0x74, 0x79,
+	0x20, 0x74, 0x72, 0x65, 0x65, 0x20, 0x63, 0x6f,
+	0x6d, 0x6d, 0x69, 0x74, 0x0a, 0x6d, 0x6d, 0x51,
+	0x9a, 0xc9, 0x11, 0x76, 0x61, 0xa3, 0x89, 0x49,
+	0xb7, 0xa1, 0x58, 0xc6, 0x1d, 0x8c, 0x33, 0x75,
+	0x8d, 0x7e, 0x4d, 0x8e, 0x58, 0x91, 0xf8, 0x5c,
+	0x57, 0xd9, 0x89, 0x9e, 0xb8, 0xd2, 0x9a, 0xd8,
+	0xc9
+};
+
+static const struct fast_pack fast_packs[] = {
+	{
+		.format_id = GIT_SHA1_FORMAT_ID,
+		.suffix = fast_pack_sha1_suffix,
+		.suffix_len = sizeof(fast_pack_sha1_suffix),
+		.commit_oid = "aac43daf40d0377af31aa9c798a4ae8a31b55c1d",
+	},
+	{
+		.format_id = GIT_SHA256_FORMAT_ID,
+		.suffix = fast_pack_sha256_suffix,
+		.suffix_len = sizeof(fast_pack_sha256_suffix),
+		.commit_oid = "63c46ca51267b1d45be69a044bb84b4bf0559f09"
+			      "d727f861d2ae94ddebdddbc9",
+	},
+};
+
+/*
+ * Try the fast path for known blob sizes.  Returns 1 if the pack was
+ * written from precomputed constants, 0 if the caller should fall
+ * through to the generic path.
+ */
+static int generate_fast_pack(const char *path, size_t blob_size,
+			      const struct git_hash_algo *algo)
+{
+	const struct fast_pack *fp = NULL;
+	FILE *f;
+	size_t i;
+
+	if (blob_size != FAST_PACK_4G1_BLOB_SIZE)
+		return 0;
+
+	for (i = 0; i < ARRAY_SIZE(fast_packs); i++) {
+		if (fast_packs[i].format_id == algo->format_id) {
+			fp = &fast_packs[i];
+			break;
+		}
+	}
+	if (!fp)
+		return 0;
+
+	f = xfopen(path, "wb");
+
+	fwrite_or_die(f, fast_pack_prefix, sizeof(fast_pack_prefix));
+
+	/* First full block: 0xffff zero bytes (header already in prefix) */
+	fwrite_or_die(f, zeros, BLOCK_SIZE);
+
+	/* Remaining non-final full blocks */
+	for (i = 1; i < FAST_PACK_4G1_N_FULL_BLOCKS; i++) {
+		fwrite_or_die(f, fast_pack_block_header,
+			      sizeof(fast_pack_block_header));
+		fwrite_or_die(f, zeros, BLOCK_SIZE);
+	}
+
+	/* Final block (2 data bytes) + adler32 */
+	fwrite_or_die(f, fast_pack_final_block,
+		      sizeof(fast_pack_final_block));
+
+	/* Tree, commits, and pack checksum */
+	fwrite_or_die(f, fp->suffix, fp->suffix_len);
+
+	if (fclose(f))
+		die_errno(_("could not close '%s'"), path);
+
+	printf("%s\n", fp->commit_oid);
+	return 1;
+}
+
+/*
+ * Generate a pack file with a single large (>4GB) reachable object.
+ *
+ * Creates:
+ *   1. A large blob (all NUL bytes)
+ *   2. A tree containing that blob as "file"
+ *   3. A commit using that tree
+ *   4. The empty tree
+ *   5. A child commit using the empty tree
+ *
+ * This is useful for testing that Git can handle objects larger than 4GB.
+ */
+static int generate_pack_with_large_object(const char *path, size_t blob_size,
+					   const struct git_hash_algo *algo)
+{
+	FILE *f;
+	struct git_hash_ctx pack_ctx;
+	unsigned char pack_hash[GIT_MAX_RAWSZ];
+	struct object_id blob_oid, tree_oid, commit_oid, empty_tree_oid, final_commit_oid;
+	struct strbuf buf = STRBUF_INIT;
+	const uint32_t object_count = 5;
+	struct pack_header pack_header = {
+		.hdr_signature = htonl(PACK_SIGNATURE),
+		.hdr_version = htonl(PACK_VERSION),
+		.hdr_entries = htonl(object_count),
+	};
+
+	if (generate_fast_pack(path, blob_size, algo))
+		return 0;
+
+	f = xfopen(path, "wb");
+
+	algo->init_fn(&pack_ctx);
+
+	/* Write pack header */
+	fwrite_or_die(f, &pack_header, sizeof(pack_header));
+	algo->update_fn(&pack_ctx, &pack_header, sizeof(pack_header));
+
+	/* 1. Write the large blob */
+	write_pack_object(f, &pack_ctx, OBJ_BLOB, NULL, blob_size, &blob_oid, algo);
+
+	/* 2. Write tree containing the blob as "file" */
+	strbuf_addf(&buf, "100644 file%c", '\0');
+	strbuf_add(&buf, blob_oid.hash, algo->rawsz);
+	write_pack_object(f, &pack_ctx, OBJ_TREE, buf.buf, buf.len, &tree_oid, algo);
+
+	/* 3. Write commit using that tree */
+	strbuf_reset(&buf);
+	strbuf_addf(&buf,
+		    "tree %s\n"
+		    "author A U Thor <author@example.com> 1234567890 +0000\n"
+		    "committer C O Mitter <committer@example.com> 1234567890 +0000\n"
+		    "\n"
+		    "Large blob commit\n",
+		    oid_to_hex(&tree_oid));
+	write_pack_object(f, &pack_ctx, OBJ_COMMIT, buf.buf, buf.len, &commit_oid, algo);
+
+	/* 4. Write the empty tree */
+	write_pack_object(f, &pack_ctx, OBJ_TREE, "", 0, &empty_tree_oid, algo);
+
+	/* 5. Write final commit using empty tree, with previous commit as parent */
+	strbuf_reset(&buf);
+	strbuf_addf(&buf,
+		    "tree %s\n"
+		    "parent %s\n"
+		    "author A U Thor <author@example.com> 1234567890 +0000\n"
+		    "committer C O Mitter <committer@example.com> 1234567890 +0000\n"
+		    "\n"
+		    "Empty tree commit\n",
+		    oid_to_hex(&empty_tree_oid),
+		    oid_to_hex(&commit_oid));
+	write_pack_object(f, &pack_ctx, OBJ_COMMIT, buf.buf, buf.len, &final_commit_oid, algo);
+
+	/* Write pack trailer (checksum) */
+	algo->final_fn(pack_hash, &pack_ctx);
+	fwrite_or_die(f, pack_hash, algo->rawsz);
+	if (fclose(f))
+		die_errno(_("could not close '%s'"), path);
+
+	strbuf_release(&buf);
+
+	/* Print the final commit OID so caller can set up refs */
+	printf("%s\n", oid_to_hex(&final_commit_oid));
+
+	return 0;
+}
+
+static int cmd__synthesize__pack(int argc, const char **argv,
+				 const char *prefix UNUSED,
+				 struct repository *repo)
+{
+	int non_git;
+	int reachable_large = 0;
+	const struct git_hash_algo *algo;
+	size_t blob_size;
+	uintmax_t blob_size_u;
+	const char *path;
+	const char * const usage[] = {
+		"test-tool synthesize pack "
+		"--reachable-large <blob-size> <filename>",
+		NULL
+	};
+	struct option options[] = {
+		OPT_BOOL(0, "reachable-large", &reachable_large,
+			 N_("write a pack with a single reachable large blob")),
+		OPT_END()
+	};
+
+	setup_git_directory_gently(the_repository, &non_git);
+	repo = the_repository;
+	algo = unsafe_hash_algo(repo->hash_algo);
+
+	argc = parse_options(argc, argv, NULL, options, usage,
+			     PARSE_OPT_KEEP_ARGV0);
+	if (argc != 3 || !reachable_large)
+		usage_with_options(usage, options);
+
+	if (!git_parse_unsigned(argv[1], &blob_size_u,
+				maximum_unsigned_value_of_type(size_t)))
+		die(_("'%s' is not a valid blob size"), argv[1]);
+	blob_size = blob_size_u;
+	path = argv[2];
+
+	return !!generate_pack_with_large_object(path, blob_size, algo);
+}
+
+int cmd__synthesize(int argc, const char **argv)
+{
+	const char *prefix = NULL;
+	char const * const synthesize_usage[] = {
+		"test-tool synthesize pack <options>",
+		NULL,
+	};
+	parse_opt_subcommand_fn *fn = NULL;
+	struct option options[] = {
+		OPT_SUBCOMMAND("pack", &fn, cmd__synthesize__pack),
+		OPT_END()
+	};
+	argc = parse_options(argc, argv, prefix, options, synthesize_usage, 0);
+	return !!fn(argc, argv, prefix, NULL);
+}
diff --git a/t/helper/test-tool.c b/t/helper/test-tool.c
index a7abc61..b71a22b 100644
--- a/t/helper/test-tool.c
+++ b/t/helper/test-tool.c
@@ -82,6 +82,7 @@ static struct test_cmd cmds[] = {
 	{ "submodule-config", cmd__submodule_config },
 	{ "submodule-nested-repo-config", cmd__submodule_nested_repo_config },
 	{ "subprocess", cmd__subprocess },
+	{ "synthesize", cmd__synthesize },
 	{ "trace2", cmd__trace2 },
 	{ "truncate", cmd__truncate },
 	{ "userdiff", cmd__userdiff },
diff --git a/t/helper/test-tool.h b/t/helper/test-tool.h
index 7f150fa..f2885b3 100644
--- a/t/helper/test-tool.h
+++ b/t/helper/test-tool.h
@@ -75,6 +75,7 @@ int cmd__submodule(int argc, const char **argv);
 int cmd__submodule_config(int argc, const char **argv);
 int cmd__submodule_nested_repo_config(int argc, const char **argv);
 int cmd__subprocess(int argc, const char **argv);
+int cmd__synthesize(int argc, const char **argv);
 int cmd__trace2(int argc, const char **argv);
 int cmd__truncate(int argc, const char **argv);
 int cmd__userdiff(int argc, const char **argv);
diff --git a/t/helper/test-userdiff.c b/t/helper/test-userdiff.c
index aa3a989..fc34c58 100644
--- a/t/helper/test-userdiff.c
+++ b/t/helper/test-userdiff.c
@@ -40,7 +40,7 @@ int cmd__userdiff(int argc, const char **argv)
 		return error("unknown argument %s", argv[1]);
 
 	if (want & USERDIFF_DRIVER_TYPE_CUSTOM) {
-		setup_git_directory();
+		setup_git_directory(the_repository);
 		repo_config(the_repository, cmd__userdiff_config, NULL);
 	}
 
diff --git a/t/helper/test-write-cache.c b/t/helper/test-write-cache.c
index b37dd2c..98e1477 100644
--- a/t/helper/test-write-cache.c
+++ b/t/helper/test-write-cache.c
@@ -12,7 +12,7 @@ int cmd__write_cache(int argc, const char **argv)
 	int i, cnt = 1;
 	if (argc == 2)
 		cnt = strtol(argv[1], NULL, 0);
-	setup_git_directory();
+	setup_git_directory(the_repository);
 	repo_read_index(the_repository);
 	for (i = 0; i < cnt; i++) {
 		repo_hold_locked_index(the_repository, &index_lock,
diff --git a/t/lib-git-daemon.sh b/t/lib-git-daemon.sh
index e625692..d172aa5 100644
--- a/t/lib-git-daemon.sh
+++ b/t/lib-git-daemon.sh
@@ -85,14 +85,16 @@
 
 	# kill git-daemon child of git
 	say >&3 "Stopping git daemon ..."
+
 	kill "$GIT_DAEMON_PID"
-	wait "$GIT_DAEMON_PID" >&3 2>&4
-	ret=$?
+	ret=0; wait "$GIT_DAEMON_PID" >&3 2>&4 || ret=$?
+
 	if ! test_match_signal 15 $ret
 	then
 		error "git daemon exited with status: $ret"
 	fi
-	kill "$(cat "$GIT_DAEMON_PIDFILE")" 2>/dev/null
+
+	kill "$(cat "$GIT_DAEMON_PIDFILE")" 2>/dev/null || :
 	GIT_DAEMON_PID=
 	rm -f git_daemon_output "$GIT_DAEMON_PIDFILE"
 }
diff --git a/t/lib-git-svn.sh b/t/lib-git-svn.sh
index 2fde235..52843f6 100644
--- a/t/lib-git-svn.sh
+++ b/t/lib-git-svn.sh
@@ -15,8 +15,7 @@
 SVN_TREE=$GIT_SVN_DIR/svn-tree
 test_set_port SVNSERVE_PORT
 
-svn >/dev/null 2>&1
-if test $? -ne 1
+if ! svn help >/dev/null 2>&1
 then
 	skip_all='skipping git svn tests, svn not found'
 	test_done
@@ -27,13 +26,13 @@
 svnconf=$PWD/svnconf
 export svnconf
 
+x=0
 perl -w -e "
 use SVN::Core;
 use SVN::Repos;
 \$SVN::Core::VERSION gt '1.1.0' or exit(42);
 system(qw/svnadmin create --fs-type fsfs/, \$ENV{svnrepo}) == 0 or exit(41);
-" >&3 2>&4
-x=$?
+" >&3 2>&4 || x=$?
 if test $x -ne 0
 then
 	if test $x -eq 42; then
diff --git a/t/lib-httpd.sh b/t/lib-httpd.sh
index 4c76e81..fc64644 100644
--- a/t/lib-httpd.sh
+++ b/t/lib-httpd.sh
@@ -235,11 +235,10 @@
 
 	test_atexit stop_httpd
 
-	"$LIB_HTTPD_PATH" -d "$HTTPD_ROOT_PATH" \
+	if ! "$LIB_HTTPD_PATH" -d "$HTTPD_ROOT_PATH" \
 		-f "$TEST_PATH/apache.conf" $HTTPD_PARA \
 		-c "Listen 127.0.0.1:$LIB_HTTPD_PORT" -k start \
 		>&3 2>&4
-	if test $? -ne 0
 	then
 		cat "$HTTPD_ROOT_PATH"/error.log >&4 2>/dev/null
 		test_skip_or_die GIT_TEST_HTTPD "web server setup failed"
diff --git a/t/meson.build b/t/meson.build
index 7528e5c..2af8d01 100644
--- a/t/meson.build
+++ b/t/meson.build
@@ -6,6 +6,7 @@
   'unit-tests/u-hashmap.c',
   'unit-tests/u-list-objects-filter-options.c',
   'unit-tests/u-mem-pool.c',
+  'unit-tests/u-odb-inmemory.c',
   'unit-tests/u-oid-array.c',
   'unit-tests/u-oidmap.c',
   'unit-tests/u-oidtree.c',
@@ -397,6 +398,7 @@
   't3450-history.sh',
   't3451-history-reword.sh',
   't3452-history-split.sh',
+  't3453-history-fixup.sh',
   't3500-cherry.sh',
   't3501-revert-cherry-pick.sh',
   't3502-cherry-pick-merge.sh',
@@ -951,6 +953,7 @@
   't7702-repack-cyclic-alternate.sh',
   't7703-repack-geometric.sh',
   't7704-repack-cruft.sh',
+  't7705-repack-incremental-midx.sh',
   't7800-difftool.sh',
   't7810-grep.sh',
   't7811-grep-open.sh',
@@ -1114,6 +1117,7 @@
   't9901-git-web--browse.sh',
   't9902-completion.sh',
   't9903-bash-prompt.sh',
+  't9904-url-parse.sh',
 ]
 
 benchmarks = [
diff --git a/t/perf/p3904-stash-patch.sh b/t/perf/p3904-stash-patch.sh
new file mode 100755
index 0000000..4cfce63
--- /dev/null
+++ b/t/perf/p3904-stash-patch.sh
@@ -0,0 +1,43 @@
+#!/bin/sh
+
+test_description="Performance tests for git stash -p"
+
+. ./perf-lib.sh
+
+test_perf_fresh_repo
+
+test_expect_success "setup" '
+	mkdir files &&
+	test_seq 1 100000 | while read i; do
+		echo "content $i" >files/$i.txt || return 1
+	done &&
+	git add files/ &&
+	git commit -q -m "add tracked files" &&
+	echo modified >files/1.txt
+'
+
+test_perf "stash -p, no fsmonitor" \
+	--setup 'echo modified >files/1.txt' '
+	printf "q\n" | git stash -p >/dev/null 2>&1 || true
+'
+
+if test_have_prereq FSMONITOR_DAEMON
+then
+	test_expect_success "enable builtin fsmonitor" '
+		git config core.fsmonitor true &&
+		git fsmonitor--daemon start &&
+		git update-index --fsmonitor &&
+		git status >/dev/null 2>&1
+	'
+
+	test_perf "stash -p, builtin fsmonitor" \
+		--setup 'echo modified >files/1.txt && git status >/dev/null 2>&1' '
+		printf "q\n" | git stash -p >/dev/null 2>&1 || true
+	'
+
+	test_expect_success "stop builtin fsmonitor" '
+		git fsmonitor--daemon stop
+	'
+fi
+
+test_done
diff --git a/t/perf/p5315-pack-objects-filter.sh b/t/perf/p5315-pack-objects-filter.sh
new file mode 100755
index 0000000..445ff25
--- /dev/null
+++ b/t/perf/p5315-pack-objects-filter.sh
@@ -0,0 +1,129 @@
+#!/bin/sh
+
+test_description='Tests pack-objects performance with filters and --path-walk'
+. ./perf-lib.sh
+
+test_perf_large_repo
+
+test_expect_success 'setup filter inputs' '
+	# Sample a few depth-2 directories from the test repo to build
+	# a cone-mode sparse-checkout definition.  The sampling picks
+	# directories at evenly-spaced positions so the choice is stable
+	# and scales to repos of any shape.
+
+	git ls-tree -d HEAD >top-entries &&
+	grep "^040000" top-entries |
+		awk "{print \$4;}" >top-dirs &&
+	top_nr=$(wc -l <top-dirs) &&
+
+	while read tdir
+	do
+		git ls-tree -d --format="$tdir/%(path)" "HEAD:$tdir" || return 1
+	done <top-dirs >depth2-dirs &&
+
+	d2_nr=$(wc -l <depth2-dirs) &&
+
+	if test "$d2_nr" -ge 2
+	then
+		# Pick two directories from evenly-spaced positions.
+		first=$(sed -n "1p" depth2-dirs) &&
+		mid=$(sed -n "$((d2_nr / 2 + 1))p" depth2-dirs) &&
+
+		p1=$(dirname "$first") &&
+		p2=$(dirname "$mid") &&
+
+		# Build cone-mode sparse-checkout patterns.
+		{
+			echo "/*" &&
+			echo "!/*/" &&
+			echo "/$p1/" &&
+			echo "!/$p1/*/" &&
+			if test "$p1" != "$p2"
+			then
+				echo "/$p2/" &&
+				echo "!/$p2/*/"
+			fi &&
+			echo "/$first/" &&
+			if test "$first" != "$mid"
+			then
+				echo "/$mid/"
+			fi
+		} >sparse-patterns &&
+
+		git hash-object -w sparse-patterns >sparse-oid &&
+		echo "Sparse cone: $first $mid" &&
+		cat sparse-patterns &&
+		test_set_prereq SPARSE_OID
+	elif test "$top_nr" -ge 1
+	then
+		# Fallback: use a single top-level directory.
+		first=$(sed -n "1p" top-dirs) &&
+		{
+			echo "/*" &&
+			echo "!/*/" &&
+			echo "/$first/"
+		} >sparse-patterns &&
+
+		git hash-object -w sparse-patterns >sparse-oid &&
+		echo "Sparse cone: $first" &&
+		cat sparse-patterns &&
+		test_set_prereq SPARSE_OID
+	fi
+'
+
+test_perf 'repack (no filter)' '
+	git pack-objects --stdout --no-reuse-delta --revs --all </dev/null >pk
+'
+
+test_size 'repack size (no filter)' '
+	test_file_size pk
+'
+
+test_perf 'repack (no filter, --path-walk)' '
+	git pack-objects --stdout --no-reuse-delta --revs --all --path-walk </dev/null >pk
+'
+
+test_size 'repack size (no filter, --path-walk)' '
+	test_file_size pk
+'
+
+test_perf 'repack (blob:none)' '
+	git pack-objects --stdout --no-reuse-delta --revs --all --filter=blob:none </dev/null >pk
+'
+
+test_size 'repack size (blob:none)' '
+	test_file_size pk
+'
+
+test_perf 'repack (blob:none, --path-walk)' '
+	git pack-objects --stdout --no-reuse-delta --revs --all --path-walk \
+		--filter=blob:none </dev/null >pk
+'
+
+test_size 'repack size (blob:none, --path-walk)' '
+	test_file_size pk
+'
+
+test_perf 'repack (sparse:oid)' \
+	--prereq SPARSE_OID '
+	git pack-objects --stdout --no-reuse-delta --revs --all \
+		--filter=sparse:oid=$(cat sparse-oid) </dev/null >pk
+'
+
+test_size 'repack size (sparse:oid)' \
+	--prereq SPARSE_OID '
+	test_file_size pk
+'
+
+test_perf 'repack (sparse:oid, --path-walk)' \
+	--prereq SPARSE_OID '
+	git pack-objects --stdout --no-reuse-delta --revs --all --path-walk \
+		--filter=sparse:oid=$(cat sparse-oid) </dev/null >pk
+'
+
+test_size 'repack size (sparse:oid, --path-walk)' \
+	--prereq SPARSE_OID '
+	test_file_size pk
+'
+
+test_done
diff --git a/t/perf/p6011-rev-list-maximal.sh b/t/perf/p6011-rev-list-maximal.sh
new file mode 100755
index 0000000..e868e83
--- /dev/null
+++ b/t/perf/p6011-rev-list-maximal.sh
@@ -0,0 +1,29 @@
+#!/bin/sh
+
+test_description='Test --maximal-only and --independent options'
+
+. ./perf-lib.sh
+
+test_perf_default_repo
+
+test_expect_success 'setup' '
+	git for-each-ref --format="%(*objecttype) %(objecttype) %(objectname)" \
+		"refs/heads/*" "refs/tags/*" |
+		sed -n -e "s/^commit commit //p" -e "s/^ commit //p" |
+		head -n 50 >commits &&
+	git commit-graph write --reachable
+'
+
+test_perf 'merge-base --independent' '
+	git merge-base --independent $(cat commits) >/dev/null
+'
+
+test_perf 'rev-list --maximal-only' '
+	git rev-list --maximal-only $(cat commits) >/dev/null
+'
+
+test_perf 'rev-list --maximal-only --since' '
+	git rev-list --maximal-only --since=2000-01-01 $(cat commits) >/dev/null
+'
+
+test_done
diff --git a/t/t0005-signals.sh b/t/t0005-signals.sh
index afba0fc..84319cf 100755
--- a/t/t0005-signals.sh
+++ b/t/t0005-signals.sh
@@ -42,12 +42,12 @@
 '
 
 test_expect_success !MINGW 'a constipated git dies with SIGPIPE' '
-	OUT=$( ((large_git; echo $? 1>&3) | :) 3>&1 ) &&
+	OUT=$( ((large_git && echo 0 1>&3 || echo $? 1>&3) | :) 3>&1 ) &&
 	test_match_signal 13 "$OUT"
 '
 
 test_expect_success !MINGW 'a constipated git dies with SIGPIPE even if parent ignores it' '
-	OUT=$( ((trap "" PIPE && large_git; echo $? 1>&3) | :) 3>&1 ) &&
+	OUT=$( ((trap "" PIPE && large_git && echo 0 1>&3 || echo $? 1>&3) | :) 3>&1 ) &&
 	test_match_signal 13 "$OUT"
 '
 
diff --git a/t/t0006-date.sh b/t/t0006-date.sh
index 53ced36..9a76b84 100755
--- a/t/t0006-date.sh
+++ b/t/t0006-date.sh
@@ -155,15 +155,45 @@
 check_parse '2100-00-00 00:00:00 +11' bad
 REQUIRE_64BIT_TIME=
 
+add_time_offset() {
+	case "$3" in
+	hours)
+		unit=$(( 60*60 ))
+		;;
+	days)
+		unit=$(( 24*60*60 ))
+		;;
+	esac
+	offset=$(( $2 * unit ))
+	echo $(( $1 + offset ))
+}
+
 check_approxidate() {
+	old_date=$GIT_TEST_DATE_NOW
+	if test "$3" = "failure"
+	then
+		expection="$3"
+	else
+		expection=${4:-success}
+		offset="$3"
+	fi
+	if test -n "$offset"
+	then
+		GIT_TEST_DATE_NOW=$(add_time_offset $old_date $offset)
+		caption="$1; offset $offset"
+	else
+		caption=$1
+	fi
 	echo "$1 -> $2 +0000" >expect
-	test_expect_${3:-success} "parse approxidate ($1)" "
+	test_expect_$expection "parse approxidate ($caption)" "
 	test-tool date approxidate '$1' >actual &&
 	test_cmp expect actual
 	"
+	GIT_TEST_DATE_NOW=$old_date
 }
 
 check_approxidate now '2009-08-30 19:20:00'
+check_approxidate today '2009-08-30 00:00:00'
 check_approxidate '5 seconds ago' '2009-08-30 19:19:55'
 check_approxidate 5.seconds.ago '2009-08-30 19:19:55'
 check_approxidate 10.minutes.ago '2009-08-30 19:10:00'
@@ -179,14 +209,26 @@
 check_approxidate '3:00' '2009-08-30 03:00:00'
 check_approxidate '15:00' '2009-08-30 15:00:00'
 check_approxidate 'noon today' '2009-08-30 12:00:00'
+check_approxidate 'today at noon' '2009-08-30 12:00:00' '-12 hours'
+check_approxidate 'noon today' '2009-09-01 12:00:00' '+36 hours'
 check_approxidate 'noon yesterday' '2009-08-29 12:00:00'
+check_approxidate 'noon yesterday' '2009-08-29 12:00:00' '-12 hours'
+check_approxidate 'last Friday at noon' '2009-08-28 12:00:00'
+check_approxidate 'last Friday at noon' '2009-08-28 12:00:00' '-12 hours'
+check_approxidate 'tea last saturday' '2009-08-29 17:00:00'
+check_approxidate 'tea last saturday' '2009-08-29 17:00:00' '-12 hours'
 check_approxidate 'January 5th noon pm' '2009-01-05 12:00:00'
+check_approxidate 'January 5th noon pm' '2009-01-05 12:00:00' '-12 hours'
+check_approxidate 'January 5th today pm' '2009-01-30 12:00:00'
 check_approxidate '10am noon' '2009-08-29 12:00:00'
+check_approxidate 'January 5th yesterday' '2009-01-29 19:20:00'
+check_approxidate 'January 5th yesterday' '2008-12-31 19:20:00' '+2 days'
 
 check_approxidate 'last tuesday' '2009-08-25 19:20:00'
 check_approxidate 'July 5th' '2009-07-05 19:20:00'
 check_approxidate '06/05/2009' '2009-06-05 19:20:00'
 check_approxidate '06.05.2009' '2009-05-06 19:20:00'
+check_approxidate 'Jan 5 today' '2009-01-30 00:00:00'
 
 check_approxidate 'Jun 6, 5AM' '2009-06-06 05:00:00'
 check_approxidate '5AM Jun 6' '2009-06-06 05:00:00'
diff --git a/t/t0008-ignores.sh b/t/t0008-ignores.sh
index e716b5c..d77a179 100755
--- a/t/t0008-ignores.sh
+++ b/t/t0008-ignores.sh
@@ -122,8 +122,8 @@
 	fi
 	testname="$1" expect_all="$2" code="$3"
 
-	expect_verbose=$( echo "$expect_all" | grep -v '^::	' )
-	expect=$( echo "$expect_verbose" | sed -e 's/.*	//' )
+	expect_verbose=$(echo "$expect_all" | grep -v '^::	' || :)
+	expect=$(echo "$expect_verbose" | sed -e 's/.*	//')
 
 	test_expect_success $prereq "$testname${no_index_opt:+ with $no_index_opt}" '
 		expect "$expect" &&
diff --git a/t/t0014-alias.sh b/t/t0014-alias.sh
index 68b4903..5144b0e 100755
--- a/t/t0014-alias.sh
+++ b/t/t0014-alias.sh
@@ -128,6 +128,12 @@
 	test_grep "ran-subsection" output
 '
 
+test_expect_success 'simple dotted alias syntax still works' '
+	test_config alias.simple.dotted "!echo ran-simple-dotted" &&
+	git simple.dotted >output &&
+	test_grep "ran-simple-dotted" output
+'
+
 test_expect_success 'subsection syntax only accepts command key' '
 	test_config alias.invalid.notcommand value &&
 	test_must_fail git invalid 2>error &&
@@ -183,6 +189,12 @@
 	test_grep "förgrena" output
 '
 
+test_expect_success 'simple dotted aliases listed in help -a' '
+	test_config alias.simple.listed "!echo test" &&
+	git help -a >output &&
+	test_grep "simple.listed" output
+'
+
 test_expect_success 'empty subsection treated as no subsection' '
 	test_config "alias..something" "!echo foobar" &&
 	git something >actual &&
diff --git a/t/t0035-safe-bare-repository.sh b/t/t0035-safe-bare-repository.sh
index ae7ef09..1d3d19f 100755
--- a/t/t0035-safe-bare-repository.sh
+++ b/t/t0035-safe-bare-repository.sh
@@ -44,11 +44,16 @@
 	test_path_is_dir outer-repo/.git/modules/subn
 '
 
-test_expect_success 'safe.bareRepository unset' '
+test_expect_success !WITH_BREAKING_CHANGES 'safe.bareRepository unset' '
 	test_unconfig --global safe.bareRepository &&
 	expect_accepted_implicit -C outer-repo/bare-repo
 '
 
+test_expect_success WITH_BREAKING_CHANGES 'safe.bareRepository unset (defaults to explicit)' '
+	test_unconfig --global safe.bareRepository &&
+	expect_rejected -C outer-repo/bare-repo
+'
+
 test_expect_success 'safe.bareRepository=all' '
 	test_config_global safe.bareRepository all &&
 	expect_accepted_implicit -C outer-repo/bare-repo
@@ -63,7 +68,8 @@
 	# safe.bareRepository must not be "explicit", otherwise
 	# git config fails with "fatal: not in a git directory" (like
 	# safe.directory)
-	test_config -C outer-repo/bare-repo safe.bareRepository all &&
+	test_when_finished "git config --file outer-repo/bare-repo/config --unset safe.bareRepository" &&
+	git config --file outer-repo/bare-repo/config safe.bareRepository all &&
 	test_config_global safe.bareRepository explicit &&
 	expect_rejected -C outer-repo/bare-repo
 '
diff --git a/t/t0090-cache-tree.sh b/t/t0090-cache-tree.sh
index d901588..0964718 100755
--- a/t/t0090-cache-tree.sh
+++ b/t/t0090-cache-tree.sh
@@ -278,4 +278,12 @@
 	)
 '
 
+test_expect_success 'cache-tree is used by write-tree when valid' '
+	test_commit use-valid &&
+
+	# write-tree with a valid cache-tree should skip cache_tree_update
+	GIT_TRACE2_PERF="$(pwd)/trace.output" git write-tree &&
+	test_grep ! region_enter.*cache_tree.*update trace.output
+'
+
 test_done
diff --git a/t/t0602-reffiles-fsck.sh b/t/t0602-reffiles-fsck.sh
index 3c1f553..1325982 100755
--- a/t/t0602-reffiles-fsck.sh
+++ b/t/t0602-reffiles-fsck.sh
@@ -87,6 +87,47 @@
 	)
 '
 
+test_expect_success 'lock files should be ignored' '
+	test_when_finished "rm -rf repo" &&
+	git init repo &&
+	(
+		cd repo &&
+		git commit --allow-empty -m initial &&
+		git checkout -b branch-1 &&
+
+		touch .git/refs/heads/branch-1.lock &&
+		git refs verify 2>err &&
+		test_must_be_empty err &&
+
+		echo "foobar" >.git/refs/heads/branch-2 &&
+		test_must_fail git refs verify 2>err &&
+		cat >expect <<-EOF &&
+		error: refs/heads/branch-2: badRefContent: foobar
+		EOF
+		test_cmp expect err
+	)
+'
+
+test_expect_success 'bare lock files should not be ignored' '
+	test_when_finished "rm -rf repo" &&
+	git init repo &&
+	(
+		cd repo &&
+		git commit --allow-empty -m initial &&
+		git checkout -b branch-1 &&
+
+		# invalid refname should be reported
+		cp .git/refs/heads/branch-1 .git/refs/heads/.branch-1.lock &&
+		# invalid refname and content should be reported
+		touch .git/refs/heads/.lock &&
+
+		test_must_fail git refs verify 2>err &&
+		test_grep "error: refs/heads/.branch-1.lock: badRefName: invalid refname format" err &&
+		test_grep "error: refs/heads/.lock: badRefName: invalid refname format" err &&
+		test_grep "error: refs/heads/.lock: badRefContent: " err
+	)
+'
+
 test_expect_success 'ref name check should be adapted into fsck messages' '
 	test_when_finished "rm -rf repo" &&
 	git init repo &&
diff --git a/t/t1092-sparse-checkout-compatibility.sh b/t/t1092-sparse-checkout-compatibility.sh
index d98cb4a..8186da5 100755
--- a/t/t1092-sparse-checkout-compatibility.sh
+++ b/t/t1092-sparse-checkout-compatibility.sh
@@ -2573,4 +2573,54 @@
 	ensure_not_expanded merge -s ours merge-right
 '
 
+test_expect_success 'restore --staged with sparse definition' '
+	init_repos &&
+
+	# Stage changes within the sparse definition
+	test_all_match git checkout -b restore-staged-1 base &&
+	test_all_match git reset --soft update-deep &&
+	test_all_match git restore --staged . &&
+	test_all_match git status --porcelain=v2 &&
+	test_all_match git diff --cached
+'
+
+test_expect_success 'restore --staged with outside sparse definition' '
+	init_repos &&
+
+	# Stage changes that include paths outside the sparse definition.
+	# Although the working tree differs between full and sparse checkouts
+	# after restore, the state of the index should be the same.
+	test_all_match git checkout -b restore-staged-2 base &&
+	test_all_match git reset --soft update-folder1 &&
+	test_sparse_match git restore --staged . &&
+	git -C full-checkout restore --staged . &&
+	test_all_match git ls-files -s -- folder1 &&
+	test_all_match git diff --cached -- folder1
+'
+
+test_expect_success 'restore --staged with wildcards' '
+	init_repos &&
+
+	test_all_match git checkout -b restore-staged-3 base &&
+	test_all_match git reset --soft update-deep &&
+	test_all_match git restore --staged "deep/*" &&
+	test_all_match git status --porcelain=v2 &&
+	test_all_match git diff --cached
+'
+
+test_expect_success 'sparse-index is not expanded: restore --staged' '
+	init_repos &&
+
+	git -C sparse-index checkout -b restore-staged-exp base &&
+	git -C sparse-index reset --soft update-folder1 &&
+	ensure_not_expanded restore --staged .
+'
+
+test_expect_success 'sparse-index is not expanded: restore --source --staged' '
+	init_repos &&
+
+	git -C sparse-index checkout -b restore-source-staged base &&
+	ensure_not_expanded restore --source update-folder1 --staged .
+'
+
 test_done
diff --git a/t/t1300-config.sh b/t/t1300-config.sh
index 128971e..11fc976 100755
--- a/t/t1300-config.sh
+++ b/t/t1300-config.sh
@@ -11,6 +11,13 @@
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-terminal.sh
 
+# test-lib.sh may have added global config (e.g. safe.bareRepository)
+# that would appear in "git config --list" output and break tests
+# that expect exact config contents.
+test_expect_success 'remove global config from test-lib.sh' '
+	test_might_fail git config --global --unset-all safe.bareRepository
+'
+
 for mode in legacy subcommands
 do
 
diff --git a/t/t1301-shared-repo.sh b/t/t1301-shared-repo.sh
index 630a47a..0e0d07a 100755
--- a/t/t1301-shared-repo.sh
+++ b/t/t1301-shared-repo.sh
@@ -12,7 +12,7 @@
 . ./test-lib.sh
 
 # Remove a default ACL from the test dir if possible.
-setfacl -k . 2>/dev/null
+setfacl -k . 2>/dev/null || :
 
 # User must have read permissions to the repo -> failure on --shared=0400
 test_expect_success 'shared = 0400 (faulty permission u-w)' '
diff --git a/t/t1305-config-include.sh b/t/t1305-config-include.sh
index 6e51f89..f389257 100755
--- a/t/t1305-config-include.sh
+++ b/t/t1305-config-include.sh
@@ -350,9 +350,9 @@
 
 test_expect_success 'include cycles are detected' '
 	git init --bare cycle &&
-	git -C cycle config include.path cycle &&
+	git -C cycle --git-dir=. config include.path cycle &&
 	git config -f cycle/cycle include.path config &&
-	test_must_fail git -C cycle config --get-all test.value 2>stderr &&
+	test_must_fail git -C cycle --git-dir=. config --get-all test.value 2>stderr &&
 	grep "exceeded maximum include depth" stderr
 '
 
diff --git a/t/t1400-update-ref.sh b/t/t1400-update-ref.sh
index b2858a9..1015f33 100755
--- a/t/t1400-update-ref.sh
+++ b/t/t1400-update-ref.sh
@@ -1196,6 +1196,20 @@
 	test_must_fail git rev-parse --verify -q $c
 '
 
+test_expect_success 'stdin -z create ref fails with non commit object' '
+	printf $F "create $c" "$(test_oid 001)" >stdin &&
+	test_must_fail git update-ref -z --stdin <stdin 2>err &&
+	grep "fatal: trying to write ref ${SQ}$c${SQ} with nonexistent object" err &&
+	test_must_fail git rev-parse --verify -q $c
+'
+
+test_expect_success 'stdin -z update ref fails with non commit object' '
+	printf $F "update $b" "$(test_oid 001)" "" >stdin &&
+	test_must_fail git update-ref -z --stdin <stdin 2>err &&
+	grep "fatal: trying to write ref ${SQ}$b${SQ} with nonexistent object" err &&
+	test_must_fail git rev-parse --verify -q $c
+'
+
 test_expect_success 'stdin -z update ref works with right old value' '
 	printf $F "update $b" "$m~1" "$m" >stdin &&
 	git update-ref -z --stdin <stdin &&
diff --git a/t/t1500-rev-parse.sh b/t/t1500-rev-parse.sh
index 98c5a77..38067d9 100755
--- a/t/t1500-rev-parse.sh
+++ b/t/t1500-rev-parse.sh
@@ -337,6 +337,31 @@
 	test_cmp expect actual
 '
 
+test_expect_success 'rev-parse --bisect works with alternate terms' '
+	test_commit_bulk 6 &&
+
+	git bisect start --term-old=known --term-new=curious &&
+
+	git update-ref refs/bisect/curious-1 HEAD~1 &&
+	git update-ref refs/bisect/bad HEAD~2 &&
+	git update-ref refs/bisect/curious-3 HEAD~3 &&
+	git update-ref refs/bisect/known-3 HEAD~3 &&
+	git update-ref refs/bisect/curious-4 HEAD~4 &&
+	git update-ref refs/bisect/good HEAD~4 &&
+
+	# Note: refs/bisect/bad and refs/bisect/goood should be ignored because this
+	# is a bisect with custom terms (known/curious)
+	cat >expect <<-EOF &&
+	refs/bisect/curious-1
+	refs/bisect/curious-3
+	refs/bisect/curious-4
+	^refs/bisect/known-3
+	EOF
+
+	git rev-parse --symbolic-full-name --bisect >actual &&
+	test_cmp expect actual
+'
+
 test_expect_success '--short= truncates to the actual hash length' '
 	git rev-parse HEAD >expect &&
 	git rev-parse --short=100 HEAD >actual &&
diff --git a/t/t1517-outside-repo.sh b/t/t1517-outside-repo.sh
index c824c1a..c557f2f 100755
--- a/t/t1517-outside-repo.sh
+++ b/t/t1517-outside-repo.sh
@@ -93,6 +93,14 @@
 	test_cmp expect actual
 '
 
+test_expect_success 'hash object exceeding bigFileThreshold outside repository' '
+	(
+		cd non-repo &&
+		echo foo >foo &&
+		git -c core.bigFileThreshold=1 hash-object --stdin <foo
+	)
+'
+
 test_expect_success 'stripspace outside repository' '
 	nongit git stripspace -s </dev/null
 '
@@ -114,7 +122,8 @@
 	archimport | citool | credential-netrc | credential-libsecret | \
 	credential-osxkeychain | cvsexportcommit | cvsimport | cvsserver | \
 	daemon | \
-	difftool--helper | filter-branch | fsck-objects | get-tar-commit-id | \
+	difftool--helper | filter-branch | format-rev | fsck-objects | \
+	get-tar-commit-id | \
 	gui | gui--askpass | \
 	http-backend | http-fetch | http-push | init-db | \
 	merge-octopus | merge-one-file | merge-resolve | mergetool | \
diff --git a/t/t1800-hook.sh b/t/t1800-hook.sh
index 96749fc..0132e77 100755
--- a/t/t1800-hook.sh
+++ b/t/t1800-hook.sh
@@ -6,21 +6,72 @@
 . "$TEST_DIRECTORY"/lib-terminal.sh
 
 setup_hooks () {
-	test_config hook.ghi.command "/path/ghi"
-	test_config hook.ghi.event pre-commit --add
-	test_config hook.ghi.event test-hook --add
-	test_config_global hook.def.command "/path/def"
+	test_config hook.ghi.command "/path/ghi" &&
+	test_config hook.ghi.event pre-commit --add &&
+	test_config hook.ghi.event test-hook --add &&
+	test_config_global hook.def.command "/path/def" &&
 	test_config_global hook.def.event pre-commit --add
 }
 
 setup_hookdir () {
-	mkdir .git/hooks
-	write_script .git/hooks/pre-commit <<-EOF
+	mkdir -p .git/hooks &&
+	write_script .git/hooks/pre-commit <<-EOF &&
 	echo \"Legacy Hook\"
 	EOF
 	test_when_finished rm -rf .git/hooks
 }
 
+# write_sentinel_hook <path> [sentinel]
+#
+# Writes a hook that marks itself as started, sleeps for a few seconds, then
+# marks itself done. The sleep must be long enough that sentinel_detector can
+# observe <sentinel>.started before <sentinel>.done appears when both hooks
+# run concurrently in parallel mode.
+write_sentinel_hook () {
+	sentinel="${2:-sentinel}"
+	write_script "$1" <<-EOF
+	touch ${sentinel}.started &&
+	sleep 2 &&
+	touch ${sentinel}.done
+	EOF
+}
+
+# sentinel_detector <sentinel> <output>
+#
+# Returns a shell command string suitable for use as hook.<name>.command.
+# The detector must be registered after the sentinel:
+# 1. In serial mode, the sentinel has completed (and <sentinel>.done exists)
+#    before the detector starts.
+# 2. In parallel mode, both run concurrently so <sentinel>.done has not appeared
+#    yet and the detector just sees <sentinel>.started.
+#
+# At start, poll until <sentinel>.started exists to absorb startup jitter, then
+# write to <output>:
+# 1. 'serial'   if <sentinel>.done exists (sentinel finished before we started),
+# 2. 'parallel' if only <sentinel>.started exists (sentinel still running),
+# 3. 'timeout'  if <sentinel>.started never appeared.
+#
+# The command ends with ':' so when git appends "$@" for hooks that receive
+# positional arguments (e.g. pre-push), the result ': "$@"' is valid shell
+# rather than a syntax error 'fi "$@"'.
+sentinel_detector () {
+	cat <<-EOF
+	i=0
+	while ! test -f ${1}.started && test \$i -lt 10; do
+	    sleep 1
+	    i=\$((i+1))
+	done
+	if test -f ${1}.done; then
+	    echo serial >${2}
+	elif test -f ${1}.started; then
+	    echo parallel >${2}
+	else
+	    echo timeout >${2}
+	fi
+	:
+	EOF
+}
+
 test_expect_success 'git hook usage' '
 	test_expect_code 129 git hook &&
 	test_expect_code 129 git hook run &&
@@ -217,10 +268,20 @@
 '
 
 test_hook_tty () {
-	cat >expect <<-\EOF
-	STDOUT TTY
-	STDERR TTY
-	EOF
+	expect_tty=$1
+	shift
+
+	if test "$expect_tty" != "no_tty"; then
+		cat >expect <<-\EOF
+		STDOUT TTY
+		STDERR TTY
+		EOF
+	else
+		cat >expect <<-\EOF
+		STDOUT NO TTY
+		STDERR NO TTY
+		EOF
+	fi
 
 	test_when_finished "rm -rf repo" &&
 	git init repo &&
@@ -238,12 +299,21 @@
 	test_cmp expect repo/actual
 }
 
-test_expect_success TTY 'git hook run: stdout and stderr are connected to a TTY' '
-	test_hook_tty hook run pre-commit
+test_expect_success TTY 'git hook run -j1: stdout and stderr are connected to a TTY' '
+	# hooks running sequentially (-j1) are always connected to the tty for
+	# optimum real-time performance.
+	test_hook_tty tty hook run -j1 pre-commit
+'
+
+test_expect_success TTY 'git hook run -jN: stdout and stderr are not connected to a TTY' '
+	# Hooks are not connected to the tty when run in parallel, instead they
+	# output to a pipe through which run-command collects and de-interlaces
+	# their outputs, which then gets passed either to the tty or a sideband.
+	test_hook_tty no_tty hook run -j2 pre-commit
 '
 
 test_expect_success TTY 'git commit: stdout and stderr are connected to a TTY' '
-	test_hook_tty commit -m"B.new"
+	test_hook_tty tty commit -m"B.new"
 '
 
 test_expect_success 'git hook list orders by config order' '
@@ -658,4 +728,504 @@
 	check_stdout_merged_to_stderr push-to-checkout
 '
 
+test_expect_success 'parallel hook output is not interleaved' '
+	test_when_finished "rm -rf .git/hooks" &&
+
+	write_script .git/hooks/test-hook <<-EOF &&
+	echo "Hook 1 Start"
+	sleep 1
+	echo "Hook 1 End"
+	EOF
+
+	test_config hook.hook-2.event test-hook &&
+	test_config hook.hook-2.command \
+		    "echo \"Hook 2 Start\"; sleep 2; echo \"Hook 2 End\"" &&
+	test_config hook.hook-2.parallel true &&
+	test_config hook.hook-3.event test-hook &&
+	test_config hook.hook-3.command \
+		    "echo \"Hook 3 Start\"; sleep 3; echo \"Hook 3 End\"" &&
+	test_config hook.hook-3.parallel true &&
+
+	git hook run --allow-unknown-hook-name -j3 test-hook >out 2>err.parallel &&
+
+	# Verify Hook 1 output is grouped
+	sed -n "/Hook 1 Start/,/Hook 1 End/p" err.parallel >hook1_out &&
+	test_line_count = 2 hook1_out &&
+
+	# Verify Hook 2 output is grouped
+	sed -n "/Hook 2 Start/,/Hook 2 End/p" err.parallel >hook2_out &&
+	test_line_count = 2 hook2_out &&
+
+	# Verify Hook 3 output is grouped
+	sed -n "/Hook 3 Start/,/Hook 3 End/p" err.parallel >hook3_out &&
+	test_line_count = 2 hook3_out
+'
+
+test_expect_success 'git hook run -j1 runs hooks in series' '
+	test_when_finished "rm -rf .git/hooks" &&
+
+	test_config hook.series-1.event "test-hook" &&
+	test_config hook.series-1.command "echo 1" --add &&
+	test_config hook.series-2.event "test-hook" &&
+	test_config hook.series-2.command "echo 2" --add &&
+
+	mkdir -p .git/hooks &&
+	write_script .git/hooks/test-hook <<-EOF &&
+	echo 3
+	EOF
+
+	cat >expected <<-\EOF &&
+	1
+	2
+	3
+	EOF
+
+	git hook run --allow-unknown-hook-name -j1 test-hook 2>actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'git hook run -j2 runs hooks in parallel' '
+	test_when_finished "rm -f sentinel.started sentinel.done hook.order" &&
+	test_when_finished "rm -rf .git/hooks" &&
+
+	mkdir -p .git/hooks &&
+	write_sentinel_hook .git/hooks/test-hook &&
+
+	test_config hook.hook-2.event test-hook &&
+	test_config hook.hook-2.command \
+	    "$(sentinel_detector sentinel hook.order)" &&
+	test_config hook.hook-2.parallel true &&
+
+	git hook run --allow-unknown-hook-name -j2 test-hook >out 2>err &&
+	echo parallel >expect &&
+	test_cmp expect hook.order
+'
+
+test_expect_success 'git hook run -j2 overrides parallel=false' '
+	test_when_finished "rm -f sentinel.started sentinel.done hook.order" &&
+	test_config hook.hook-1.event test-hook &&
+	test_config hook.hook-1.command \
+	    "touch sentinel.started; sleep 2; touch sentinel.done" &&
+	# hook-1 intentionally has no parallel=true
+	test_config hook.hook-2.event test-hook &&
+	test_config hook.hook-2.command \
+	    "$(sentinel_detector sentinel hook.order)" &&
+	# hook-2 also has no parallel=true
+
+	# -j2 overrides parallel=false; hooks run in parallel with a warning.
+	git hook run --allow-unknown-hook-name -j2 test-hook >out 2>err &&
+	echo parallel >expect &&
+	test_cmp expect hook.order
+'
+
+test_expect_success 'git hook run -j2 warns for hooks not marked parallel=true' '
+	test_config hook.hook-1.event test-hook &&
+	test_config hook.hook-1.command "true" &&
+	test_config hook.hook-2.event test-hook &&
+	test_config hook.hook-2.command "true" &&
+	# neither hook has parallel=true
+
+	git hook run --allow-unknown-hook-name -j2 test-hook >out 2>err &&
+	grep "hook .hook-1. is not marked as parallel=true" err &&
+	grep "hook .hook-2. is not marked as parallel=true" err
+'
+
+test_expect_success 'hook.jobs=1 config runs hooks in series' '
+	test_when_finished "rm -f sentinel.started sentinel.done hook.order" &&
+
+	# Use two configured hooks so the execution order is deterministic:
+	# hook-1 (sentinel) is listed before hook-2 (detector), so hook-1
+	# always runs first even in serial mode.
+	test_config hook.hook-1.event test-hook &&
+	test_config hook.hook-1.command \
+	    "touch sentinel.started; sleep 2; touch sentinel.done" &&
+	test_config hook.hook-2.event test-hook &&
+	test_config hook.hook-2.command \
+	    "$(sentinel_detector sentinel hook.order)" &&
+
+	test_config hook.jobs 1 &&
+
+	git hook run --allow-unknown-hook-name test-hook >out 2>err &&
+	echo serial >expect &&
+	test_cmp expect hook.order
+'
+
+test_expect_success 'hook.jobs=2 config runs hooks in parallel' '
+	test_when_finished "rm -f sentinel.started sentinel.done hook.order" &&
+	test_when_finished "rm -rf .git/hooks" &&
+
+	mkdir -p .git/hooks &&
+	write_sentinel_hook .git/hooks/test-hook &&
+
+	test_config hook.hook-2.event test-hook &&
+	test_config hook.hook-2.command \
+	    "$(sentinel_detector sentinel hook.order)" &&
+	test_config hook.hook-2.parallel true &&
+
+	test_config hook.jobs 2 &&
+
+	git hook run --allow-unknown-hook-name test-hook >out 2>err &&
+	echo parallel >expect &&
+	test_cmp expect hook.order
+'
+
+test_expect_success 'hook.<name>.parallel=true enables parallel execution' '
+	test_when_finished "rm -f sentinel.started sentinel.done hook.order" &&
+	test_config hook.hook-1.event test-hook &&
+	test_config hook.hook-1.command \
+	    "touch sentinel.started; sleep 2; touch sentinel.done" &&
+	test_config hook.hook-1.parallel true &&
+	test_config hook.hook-2.event test-hook &&
+	test_config hook.hook-2.command \
+	    "$(sentinel_detector sentinel hook.order)" &&
+	test_config hook.hook-2.parallel true &&
+
+	test_config hook.jobs 2 &&
+
+	git hook run --allow-unknown-hook-name test-hook >out 2>err &&
+	echo parallel >expect &&
+	test_cmp expect hook.order
+'
+
+test_expect_success 'hook.<name>.parallel=false (default) forces serial execution' '
+	test_when_finished "rm -f sentinel.started sentinel.done hook.order" &&
+	test_config hook.hook-1.event test-hook &&
+	test_config hook.hook-1.command \
+	    "touch sentinel.started; sleep 2; touch sentinel.done" &&
+	test_config hook.hook-2.event test-hook &&
+	test_config hook.hook-2.command \
+	    "$(sentinel_detector sentinel hook.order)" &&
+
+	test_config hook.jobs 2 &&
+
+	git hook run --allow-unknown-hook-name test-hook >out 2>err &&
+	echo serial >expect &&
+	test_cmp expect hook.order
+'
+
+test_expect_success 'one non-parallel hook forces the whole event to run serially' '
+	test_when_finished "rm -f sentinel.started sentinel.done hook.order" &&
+	test_config hook.hook-1.event test-hook &&
+	test_config hook.hook-1.command \
+	    "touch sentinel.started; sleep 2; touch sentinel.done" &&
+	test_config hook.hook-1.parallel true &&
+	test_config hook.hook-2.event test-hook &&
+	test_config hook.hook-2.command \
+	    "$(sentinel_detector sentinel hook.order)" &&
+	# hook-2 has no parallel=true: should force serial for all
+
+	test_config hook.jobs 2 &&
+
+	git hook run --allow-unknown-hook-name test-hook >out 2>err &&
+	echo serial >expect &&
+	test_cmp expect hook.order
+'
+
+test_expect_success 'client hooks: pre-push parallel execution merges stdout to stderr' '
+	test_when_finished "rm -rf remote-par stdout.actual stderr.actual" &&
+	git init --bare remote-par &&
+	git remote add origin-par remote-par &&
+	test_commit par-commit &&
+	mkdir -p .git/hooks &&
+	setup_hooks pre-push &&
+	test_config hook.jobs 2 &&
+	git push origin-par HEAD:main >stdout.actual 2>stderr.actual &&
+	check_stdout_merged_to_stderr pre-push
+'
+
+test_expect_success 'client hooks: pre-push runs in parallel when hook.jobs > 1' '
+	test_when_finished "rm -rf repo-parallel remote-parallel" &&
+	git init --bare remote-parallel &&
+	git init repo-parallel &&
+	git -C repo-parallel remote add origin ../remote-parallel &&
+	test_commit -C repo-parallel A &&
+
+	write_sentinel_hook repo-parallel/.git/hooks/pre-push &&
+	git -C repo-parallel config hook.hook-2.event pre-push &&
+	git -C repo-parallel config hook.hook-2.command \
+	    "$(sentinel_detector sentinel hook.order)" &&
+	git -C repo-parallel config hook.hook-2.parallel true &&
+
+	git -C repo-parallel config hook.jobs 2 &&
+
+	git -C repo-parallel push origin HEAD >out 2>err &&
+	echo parallel >expect &&
+	test_cmp expect repo-parallel/hook.order
+'
+
+test_expect_success 'hook.jobs=2 is ignored for force-serial hooks (pre-commit)' '
+	test_when_finished "rm -f sentinel.started sentinel.done hook.order" &&
+	test_config hook.hook-1.event pre-commit &&
+	test_config hook.hook-1.command \
+	    "touch sentinel.started; sleep 2; touch sentinel.done" &&
+	test_config hook.hook-1.parallel true &&
+	test_config hook.hook-2.event pre-commit &&
+	test_config hook.hook-2.command \
+	    "$(sentinel_detector sentinel hook.order)" &&
+	test_config hook.hook-2.parallel true &&
+	test_config hook.jobs 2 &&
+	git commit --allow-empty -m "test: verify force-serial on pre-commit" &&
+	echo serial >expect &&
+	test_cmp expect hook.order
+'
+
+test_expect_success 'hook.<event>.jobs overrides hook.jobs for that event' '
+	test_when_finished "rm -f sentinel.started sentinel.done hook.order" &&
+	test_config hook.hook-1.event test-hook &&
+	test_config hook.hook-1.command \
+	    "touch sentinel.started; sleep 2; touch sentinel.done" &&
+	test_config hook.hook-1.parallel true &&
+	test_config hook.hook-2.event test-hook &&
+	test_config hook.hook-2.command \
+	    "$(sentinel_detector sentinel hook.order)" &&
+	test_config hook.hook-2.parallel true &&
+
+	# Global hook.jobs=1 (serial), but per-event override allows parallel.
+	test_config hook.jobs 1 &&
+	test_config hook.test-hook.jobs 2 &&
+
+	git hook run --allow-unknown-hook-name test-hook >out 2>err &&
+	echo parallel >expect &&
+	test_cmp expect hook.order
+'
+
+test_expect_success 'hook.<event>.jobs=1 forces serial even when hook.jobs>1' '
+	test_when_finished "rm -f sentinel.started sentinel.done hook.order" &&
+	test_config hook.hook-1.event test-hook &&
+	test_config hook.hook-1.command \
+	    "touch sentinel.started; sleep 2; touch sentinel.done" &&
+	test_config hook.hook-1.parallel true &&
+	test_config hook.hook-2.event test-hook &&
+	test_config hook.hook-2.command \
+	    "$(sentinel_detector sentinel hook.order)" &&
+	test_config hook.hook-2.parallel true &&
+
+	# Global hook.jobs=4 allows parallel, but per-event override forces serial.
+	test_config hook.jobs 4 &&
+	test_config hook.test-hook.jobs 1 &&
+
+	git hook run --allow-unknown-hook-name test-hook >out 2>err &&
+	echo serial >expect &&
+	test_cmp expect hook.order
+'
+
+test_expect_success 'hook.<event>.jobs still requires hook.<name>.parallel=true' '
+	test_when_finished "rm -f sentinel.started sentinel.done hook.order" &&
+	test_config hook.hook-1.event test-hook &&
+	test_config hook.hook-1.command \
+	    "touch sentinel.started; sleep 2; touch sentinel.done" &&
+	# hook-1 intentionally has no parallel=true
+	test_config hook.hook-2.event test-hook &&
+	test_config hook.hook-2.command \
+	    "$(sentinel_detector sentinel hook.order)" &&
+	# hook-2 also has no parallel=true
+
+	# Per-event jobs=2 but no hook has parallel=true: must still run serially.
+	test_config hook.test-hook.jobs 2 &&
+
+	git hook run --allow-unknown-hook-name test-hook >out 2>err &&
+	echo serial >expect &&
+	test_cmp expect hook.order
+'
+
+test_expect_success 'hook.<friendly-name>.jobs warns when name has .command' '
+	test_config hook.my-hook.command "true" &&
+	test_config hook.my-hook.jobs 2 &&
+	git hook run --allow-unknown-hook-name --ignore-missing test-hook >out 2>err &&
+	test_grep "hook.my-hook.jobs.*friendly-name" err
+'
+
+test_expect_success 'hook.<friendly-name>.jobs warns when name has .event' '
+	test_config hook.my-hook.event test-hook &&
+	test_config hook.my-hook.command "true" &&
+	test_config hook.my-hook.jobs 2 &&
+	git hook run --allow-unknown-hook-name --ignore-missing test-hook >out 2>err &&
+	test_grep "hook.my-hook.jobs.*friendly-name" err
+'
+
+test_expect_success 'hook.<friendly-name>.jobs warns when name has .parallel' '
+	test_config hook.my-hook.event test-hook &&
+	test_config hook.my-hook.command "true" &&
+	test_config hook.my-hook.parallel true &&
+	test_config hook.my-hook.jobs 2 &&
+	git hook run --allow-unknown-hook-name --ignore-missing test-hook >out 2>err &&
+	test_grep "hook.my-hook.jobs.*friendly-name" err
+'
+
+test_expect_success 'hook.<event>.jobs does not warn for a real event name' '
+	test_config hook.test-hook.jobs 2 &&
+	git hook run --allow-unknown-hook-name --ignore-missing test-hook >out 2>err &&
+	test_grep ! "friendly-name" err
+'
+
+test_expect_success 'hook.jobs=-1 resolves to online_cpus()' '
+	test_config hook.hook-1.event test-hook &&
+	test_config hook.hook-1.command "true" &&
+	test_config hook.hook-1.parallel true &&
+
+	test_config hook.jobs -1 &&
+
+	cpus=$(test-tool online-cpus) &&
+	GIT_TRACE2_EVENT="$(pwd)/trace.txt" \
+		git hook run --allow-unknown-hook-name test-hook >out 2>err &&
+	grep "\"region_enter\".*\"hook\".*\"test-hook\".*\"max:$cpus\"" trace.txt
+'
+
+test_expect_success 'hook.<event>.jobs=-1 resolves to online_cpus()' '
+	test_config hook.hook-1.event test-hook &&
+	test_config hook.hook-1.command "true" &&
+	test_config hook.hook-1.parallel true &&
+
+	test_config hook.test-hook.jobs -1 &&
+
+	cpus=$(test-tool online-cpus) &&
+	GIT_TRACE2_EVENT="$(pwd)/trace.txt" \
+		git hook run --allow-unknown-hook-name test-hook >out 2>err &&
+	grep "\"region_enter\".*\"hook\".*\"test-hook\".*\"max:$cpus\"" trace.txt
+'
+
+test_expect_success 'git hook run -j-1 resolves to online_cpus()' '
+	test_config hook.hook-1.event test-hook &&
+	test_config hook.hook-1.command "true" &&
+	test_config hook.hook-1.parallel true &&
+
+	cpus=$(test-tool online-cpus) &&
+	GIT_TRACE2_EVENT="$(pwd)/trace.txt" \
+		git hook run --allow-unknown-hook-name -j-1 test-hook >out 2>err &&
+	grep "\"region_enter\".*\"hook\".*\"test-hook\".*\"max:$cpus\"" trace.txt
+'
+
+test_expect_success 'hook.jobs rejects values less than -1' '
+	test_config hook.jobs -2 &&
+	git hook run --allow-unknown-hook-name --ignore-missing test-hook >out 2>err &&
+	test_grep "hook.jobs must be a positive integer or -1" err
+'
+
+test_expect_success 'hook.<event>.jobs rejects values less than -1' '
+	test_config hook.test-hook.jobs -5 &&
+	git hook run --allow-unknown-hook-name --ignore-missing test-hook >out 2>err &&
+	test_grep "hook.test-hook.jobs must be a positive integer or -1" err
+'
+
+test_expect_success 'hook.<event>.enabled=false skips all hooks for event' '
+	test_config hook.hook-1.event test-hook &&
+	test_config hook.hook-1.command "echo ran" &&
+	test_config hook.test-hook.enabled false &&
+	git hook run --allow-unknown-hook-name test-hook >out 2>err &&
+	test_must_be_empty out
+'
+
+test_expect_success 'hook.<event>.enabled=true does not suppress hooks' '
+	test_config hook.hook-1.event test-hook &&
+	test_config hook.hook-1.command "echo ran" &&
+	test_config hook.test-hook.enabled true &&
+	git hook run --allow-unknown-hook-name test-hook >out 2>err &&
+	test_grep "ran" err
+'
+
+test_expect_success 'hook.<event>.enabled=false does not affect other events' '
+	test_config hook.hook-1.event test-hook &&
+	test_config hook.hook-1.command "echo ran" &&
+	test_config hook.other-event.enabled false &&
+	git hook run --allow-unknown-hook-name test-hook >out 2>err &&
+	test_grep "ran" err
+'
+
+test_expect_success 'hook.<friendly-name>.enabled=false still disables that hook' '
+	test_config hook.hook-1.event test-hook &&
+	test_config hook.hook-1.command "echo hook-1" &&
+	test_config hook.hook-2.event test-hook &&
+	test_config hook.hook-2.command "echo hook-2" &&
+	test_config hook.hook-1.enabled false &&
+	git hook run --allow-unknown-hook-name test-hook >out 2>err &&
+	test_grep ! "hook-1" err &&
+	test_grep "hook-2" err
+'
+
+test_expect_success 'git hook list shows event-disabled hooks as event-disabled' '
+	test_config hook.hook-1.event test-hook &&
+	test_config hook.hook-1.command "echo ran" &&
+	test_config hook.hook-2.event test-hook &&
+	test_config hook.hook-2.command "echo ran" &&
+	test_config hook.test-hook.enabled false &&
+	git hook list --allow-unknown-hook-name test-hook >actual &&
+	test_grep "^event-disabled	hook-1$" actual &&
+	test_grep "^event-disabled	hook-2$" actual
+'
+
+test_expect_success 'git hook list shows scope with event-disabled' '
+	test_config hook.hook-1.event test-hook &&
+	test_config hook.hook-1.command "echo ran" &&
+	test_config hook.test-hook.enabled false &&
+	git hook list --allow-unknown-hook-name --show-scope test-hook >actual &&
+	test_grep "^local	event-disabled	hook-1$" actual
+'
+
+test_expect_success 'git hook list still shows hooks when event is disabled' '
+	test_config hook.hook-1.event test-hook &&
+	test_config hook.hook-1.command "echo ran" &&
+	test_config hook.test-hook.enabled false &&
+	git hook list --allow-unknown-hook-name test-hook >actual &&
+	test_grep "event-disabled" actual
+'
+
+test_expect_success 'friendly-name matching known event name is rejected' '
+	test_config hook.pre-commit.event pre-commit &&
+	test_config hook.pre-commit.command "echo oops" &&
+	test_must_fail git hook run pre-commit 2>err &&
+	test_grep "collides with a known event name" err
+'
+
+test_expect_success 'friendly-name matching known event name is rejected even for different event' '
+	test_config hook.pre-commit.event post-commit &&
+	test_config hook.pre-commit.command "echo oops" &&
+	test_must_fail git hook run post-commit 2>err &&
+	test_grep "collides with a known event name" err
+'
+
+test_expect_success 'friendly-name matching unknown event warns' '
+	test_config hook.test-hook.event test-hook &&
+	test_config hook.test-hook.command "echo ran" &&
+	git hook run --allow-unknown-hook-name test-hook >out 2>err &&
+	test_grep "same as its event" err
+'
+
+test_expect_success 'hooks in parallel that do not read input' '
+	# Add this to our $PATH to avoid having to write the whole trash
+	# directory into our config options, which would require quoting.
+	mkdir bin &&
+	PATH=$PWD/bin:$PATH &&
+
+	write_script bin/hook-fast <<-\EOF &&
+	# This hook does not read its input, so the parent process
+	# may see SIGPIPE if it is not ignored. It should happen
+	# relatively quickly.
+	exit 0
+	EOF
+
+	write_script bin/hook-slow <<-\EOF &&
+	# This hook is slow, so we expect it to still be running
+	# when the other hook has exited (and the parent has a pipe error
+	# writing to it).
+	#
+	# So we want to be slow enough that we expect this to happen, but not
+	# so slow that the test takes forever. 1 second is probably enough
+	# in practice (and if it is occasionally not on a loaded system, we
+	# will err on the side of having the test pass).
+	sleep 1
+	exit 0
+	EOF
+
+	git init --bare parallel.git &&
+	git -C parallel.git config hook.fast.command "hook-fast" &&
+	git -C parallel.git config hook.fast.event pre-receive &&
+	git -C parallel.git config hook.fast.parallel true &&
+	git -C parallel.git config hook.slow.command "hook-slow" &&
+	git -C parallel.git config hook.slow.event pre-receive &&
+	git -C parallel.git config hook.slow.parallel true &&
+	git -C parallel.git config hook.jobs 2 &&
+
+	git push ./parallel.git "+refs/heads/*:refs/heads/*"
+'
+
 test_done
diff --git a/t/t2000-conflict-when-checking-files-out.sh b/t/t2000-conflict-when-checking-files-out.sh
index f18616a..7b61370 100755
--- a/t/t2000-conflict-when-checking-files-out.sh
+++ b/t/t2000-conflict-when-checking-files-out.sh
@@ -23,42 +23,31 @@
 
 . ./test-lib.sh
 
-show_files() {
-	# show filesystem files, just [-dl] for type and name
-	find path? -ls |
-	sed -e 's/^[0-9]* * [0-9]* * \([-bcdl]\)[^ ]* *[0-9]* *[^ ]* *[^ ]* *[0-9]* [A-Z][a-z][a-z] [0-9][0-9] [^ ]* /fs: \1 /'
-	# what's in the cache, just mode and name
-	git ls-files --stage |
-	sed -e 's/^\([0-9]*\) [0-9a-f]* [0-3] /ca: \1 /'
-	# what's in the tree, just mode and name.
-	git ls-tree -r "$1" |
-	sed -e 's/^\([0-9]*\)	[^ ]*	[0-9a-f]*	/tr: \1 /'
-}
 
-date >path0
-mkdir path1
-date >path1/file1
+test_expect_success 'prepare files path0 and path1/file1' '
+	date >path0 &&
+	mkdir path1 &&
+	date >path1/file1 &&
+	git update-index --add path0 path1/file1
+'
 
-test_expect_success \
-    'git update-index --add various paths.' \
-    'git update-index --add path0 path1/file1'
+test_expect_success 'prepare working tree files with D/F conflicts' '
+	rm -fr path0 path1 &&
+	mkdir path0 &&
+	date >path0/file0 &&
+	date >path1
+'
 
-rm -fr path0 path1
-mkdir path0
-date >path0/file0
-date >path1
+test_expect_success 'git checkout-index without -f should fail on conflicting work tree.' '
+	test_must_fail git checkout-index -a
+'
 
-test_expect_success \
-    'git checkout-index without -f should fail on conflicting work tree.' \
-    'test_must_fail git checkout-index -a'
-
-test_expect_success \
-    'git checkout-index with -f should succeed.' \
-    'git checkout-index -f -a'
-
-test_expect_success \
-    'git checkout-index conflicting paths.' \
-    'test -f path0 && test -d path1 && test -f path1/file1'
+test_expect_success 'git checkout-index with -f should succeed.' '
+	git checkout-index -f -a &&
+	test_path_is_file path0 &&
+	test_path_is_dir path1 &&
+	test_path_is_file path1/file1
+'
 
 test_expect_success SYMLINKS 'checkout-index -f twice with --prefix' '
 	mkdir -p tar/get &&
@@ -83,53 +72,25 @@
 # path path3 is occupied by a non-directory.  With "-f" it should remove
 # the symlink path3 and create directory path3 and file path3/file1.
 
-mkdir path2
-date >path2/file0
-test_expect_success \
-    'git update-index --add path2/file0' \
-    'git update-index --add path2/file0'
-test_expect_success \
-    'writing tree out with git write-tree' \
-    'tree1=$(git write-tree)'
-test_debug 'show_files $tree1'
-
-mkdir path3
-date >path3/file1
-test_expect_success \
-    'git update-index --add path3/file1' \
-    'git update-index --add path3/file1'
-test_expect_success \
-    'writing tree out with git write-tree' \
-    'tree2=$(git write-tree)'
-test_debug 'show_files $tree2'
-
-rm -fr path3
-test_expect_success \
-    'read previously written tree and checkout.' \
-    'git read-tree -m $tree1 && git checkout-index -f -a'
-test_debug 'show_files $tree1'
-
-test_expect_success \
-    'add a symlink' \
-    'test_ln_s_add path2 path3'
-test_expect_success \
-    'writing tree out with git write-tree' \
-    'tree3=$(git write-tree)'
-test_debug 'show_files $tree3'
-
-# Morten says "Got that?" here.
-# Test begins.
-
-test_expect_success \
-    'read previously written tree and checkout.' \
-    'git read-tree $tree2 && git checkout-index -f -a'
-test_debug 'show_files $tree2'
-
-test_expect_success \
-    'checking out conflicting path with -f' \
-    'test ! -h path2 && test -d path2 &&
-     test ! -h path3 && test -d path3 &&
-     test ! -h path2/file0 && test -f path2/file0 &&
-     test ! -h path3/file1 && test -f path3/file1'
+test_expect_success 'checkout-index -f resolves symlink conflict on leading path' '
+	mkdir path2 &&
+	date >path2/file0 &&
+	git update-index --add path2/file0 &&
+	tree1=$(git write-tree) &&
+	mkdir path3 &&
+	date >path3/file1 &&
+	git update-index --add path3/file1 &&
+	tree2=$(git write-tree) &&
+	rm -fr path3 &&
+	git read-tree -m $tree1 &&
+	git checkout-index -f -a &&
+	test_ln_s_add path2 path3 &&
+	git read-tree $tree2 &&
+	git checkout-index -f -a &&
+	test_path_is_dir_not_symlink path2 &&
+	test_path_is_dir_not_symlink path3 &&
+	test_path_is_file_not_symlink path2/file0 &&
+	test_path_is_file_not_symlink path3/file1
+'
 
 test_done
diff --git a/t/t2200-add-update.sh b/t/t2200-add-update.sh
index 06e83d3..0a96655 100755
--- a/t/t2200-add-update.sh
+++ b/t/t2200-add-update.sh
@@ -200,6 +200,44 @@
 	test_cmp expect actual
 '
 
+test_expect_success 'add -u avoids rename pairing on unmerged paths' '
+	test_create_repo rename-crash &&
+	(
+		cd rename-crash &&
+		test_seq 1 100 |
+		sed "s/.*/line &: same text/" >conflict.txt &&
+		cp conflict.txt bystander.txt &&
+		git add conflict.txt bystander.txt &&
+		git commit -m "initial: two files with identical content" &&
+		main_branch=$(git symbolic-ref --short HEAD) &&
+		git checkout -b feature &&
+		sed "s/^line 50:.*/line 50: FEATURE/" \
+			conflict.txt >conflict.txt.tmp &&
+		mv conflict.txt.tmp conflict.txt &&
+		git add conflict.txt &&
+		git commit -m "feature: modify line 50" &&
+		git checkout "$main_branch" &&
+		sed "s/^line 50:.*/line 50: MAIN/" \
+			conflict.txt >conflict.txt.tmp &&
+		mv conflict.txt.tmp conflict.txt &&
+		git add conflict.txt &&
+		git commit -m "main: modify line 50 differently" &&
+		test_must_fail git merge feature &&
+		rm bystander.txt &&
+		git add -u >out &&
+		test_must_be_empty out &&
+		git ls-files -u >actual &&
+		test_must_be_empty actual &&
+		git ls-files bystander.txt conflict.txt >actual &&
+		cat >expect <<-\EOF &&
+		conflict.txt
+		EOF
+		test_cmp expect actual &&
+		git diff-files --name-only >actual &&
+		test_must_be_empty actual
+	)
+'
+
 test_expect_success '"add -u non-existent" should fail' '
 	test_must_fail git add -u non-existent &&
 	git ls-files >actual &&
diff --git a/t/t3000-ls-files-others.sh b/t/t3000-ls-files-others.sh
index b41e7f0..b4f0fbf 100755
--- a/t/t3000-ls-files-others.sh
+++ b/t/t3000-ls-files-others.sh
@@ -53,16 +53,19 @@
 
 test_expect_success 'ls-files --others' '
 	git ls-files --others >output &&
+	test_filter_gitconfig output &&
 	test_cmp expected1 output
 '
 
 test_expect_success 'ls-files --others --directory' '
 	git ls-files --others --directory >output &&
+	test_filter_gitconfig output &&
 	test_cmp expected2 output
 '
 
 test_expect_success '--no-empty-directory hides empty directory' '
 	git ls-files --others --directory --no-empty-directory >output &&
+	test_filter_gitconfig output &&
 	test_cmp expected3 output
 '
 
@@ -70,6 +73,7 @@
 	mkdir not-a-submodule &&
 	echo foo >not-a-submodule/.git &&
 	git ls-files -o >output &&
+	test_filter_gitconfig output &&
 	test_cmp expected1 output
 '
 
diff --git a/t/t3001-ls-files-others-exclude.sh b/t/t3001-ls-files-others-exclude.sh
index 4b67646..202fb8d 100755
--- a/t/t3001-ls-files-others-exclude.sh
+++ b/t/t3001-ls-files-others-exclude.sh
@@ -72,6 +72,7 @@
        --exclude-per-directory=.gitignore \
        --exclude-from=.git/ignore \
 	>output &&
+	test_filter_gitconfig output &&
 	test_cmp expect output
 '
 
@@ -84,6 +85,7 @@
        --exclude-per-directory=.gitignore \
        --exclude-from=.git/ignore \
 	>output &&
+	test_filter_gitconfig output &&
 	test_cmp expect output
 '
 
@@ -99,6 +101,7 @@
        --exclude-per-directory=.gitignore \
        --exclude-from=.git/ignore \
 	>output &&
+	test_filter_gitconfig output &&
 	test_cmp expect output
 '
 
diff --git a/t/t3002-ls-files-dashpath.sh b/t/t3002-ls-files-dashpath.sh
index 31462cb..6acaadb 100755
--- a/t/t3002-ls-files-dashpath.sh
+++ b/t/t3002-ls-files-dashpath.sh
@@ -24,6 +24,7 @@
 test_expect_success 'git ls-files without path restriction.' '
 	test_when_finished "rm -f expect" &&
 	git ls-files --others >output &&
+	test_filter_gitconfig output &&
 	cat >expect <<-\EOF &&
 	--
 	-foo
@@ -63,6 +64,7 @@
 test_expect_success 'git ls-files with no path restriction.' '
 	test_when_finished "rm -f expect" &&
 	git ls-files --others -- >output &&
+	test_filter_gitconfig output &&
 	cat >expect <<-\EOF &&
 	--
 	-foo
diff --git a/t/t3009-ls-files-others-nonsubmodule.sh b/t/t3009-ls-files-others-nonsubmodule.sh
index 963f346..dc990c2 100755
--- a/t/t3009-ls-files-others-nonsubmodule.sh
+++ b/t/t3009-ls-files-others-nonsubmodule.sh
@@ -36,6 +36,7 @@
 
 test_expect_success 'ls-files --others handles untracked git repositories' '
 	git ls-files -o >output &&
+	test_filter_gitconfig output &&
 	cat >expect <<-EOF &&
 	nonrepo-untracked-file/untracked
 	output
diff --git a/t/t3011-common-prefixes-and-directory-traversal.sh b/t/t3011-common-prefixes-and-directory-traversal.sh
index 3da5b2b..455e979 100755
--- a/t/t3011-common-prefixes-and-directory-traversal.sh
+++ b/t/t3011-common-prefixes-and-directory-traversal.sh
@@ -26,7 +26,7 @@
 '
 
 test_expect_success 'git ls-files -o shows the right entries' '
-	cat <<-EOF >expect &&
+	cat >expect <<-EOF &&
 	.gitignore
 	actual
 	an_ignored_dir/ignored
@@ -39,6 +39,7 @@
 	untracked_repo/
 	EOF
 	git ls-files -o >actual &&
+	test_filter_gitconfig actual &&
 	test_cmp expect actual
 '
 
diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh
index 3e44562..58b3bb0 100755
--- a/t/t3404-rebase-interactive.sh
+++ b/t/t3404-rebase-interactive.sh
@@ -1960,6 +1960,24 @@
 	)
 '
 
+test_expect_success '--update-refs ignores non-branch decorations' '
+	test_when_finished "git branch -D update-refs" &&
+	test_when_finished "git checkout primary" &&
+	git checkout -B update-refs no-conflict-branch &&
+	(
+		set_cat_todo_editor &&
+
+		# rebase.instructionFormat=%d loads normal log decorations before
+		# --update-refs adds its branch placeholders so we must ignore
+		# all non-local decorations.
+		test_must_fail git -c rebase.instructionFormat="%s%d" \
+			rebase -i --update-refs HEAD^ >todo
+	) &&
+	grep ^update-ref todo >actual &&
+	test_write_lines "update-ref refs/heads/no-conflict-branch" >expect &&
+	test_cmp expect actual
+'
+
 test_expect_success '--update-refs updates refs correctly' '
 	git checkout -B update-refs no-conflict-branch &&
 	git branch -f base HEAD~4 &&
diff --git a/t/t3420-rebase-autostash.sh b/t/t3420-rebase-autostash.sh
index ad3ba6a..f0bbc47 100755
--- a/t/t3420-rebase-autostash.sh
+++ b/t/t3420-rebase-autostash.sh
@@ -61,18 +61,22 @@
 	First, rewinding head to replay your work on top of it...
 	Applying: second commit
 	Applying: third commit
-	Applying autostash resulted in conflicts.
-	Your changes are safe in the stash.
-	You can run "git stash pop" or "git stash drop" at any time.
+	Your local changes are stashed, however applying them
+	resulted in conflicts.  You can either resolve the conflicts
+	and then discard the stash with "git stash drop", or, if you
+	do not want to resolve them now, run "git reset --hard" and
+	apply the local changes later by running "git stash pop".
 	EOF
 }
 
 create_expected_failure_merge () {
 	cat >expected <<-EOF
 	$(grep "^Created autostash: [0-9a-f][0-9a-f]*\$" actual)
-	Applying autostash resulted in conflicts.
-	Your changes are safe in the stash.
-	You can run "git stash pop" or "git stash drop" at any time.
+	Your local changes are stashed, however applying them
+	resulted in conflicts.  You can either resolve the conflicts
+	and then discard the stash with "git stash drop", or, if you
+	do not want to resolve them now, run "git reset --hard" and
+	apply the local changes later by running "git stash pop".
 	Successfully rebased and updated refs/heads/rebased-feature-branch.
 	EOF
 }
diff --git a/t/t3453-history-fixup.sh b/t/t3453-history-fixup.sh
new file mode 100755
index 0000000..868298e
--- /dev/null
+++ b/t/t3453-history-fixup.sh
@@ -0,0 +1,680 @@
+#!/bin/sh
+
+test_description='tests for git-history fixup subcommand'
+
+. ./test-lib.sh
+
+fixup_with_message () {
+	cat >message &&
+	write_script fake-editor.sh <<-\EOF &&
+	cp message "$1"
+	EOF
+	test_set_editor "$(pwd)"/fake-editor.sh &&
+	git history fixup --reedit-message "$@" &&
+	rm fake-editor.sh message
+}
+
+expect_changes () {
+	git log --format="%s" --numstat "$@" >actual.raw &&
+	sed '/^$/d' <actual.raw >actual &&
+	cat >expect &&
+	test_cmp expect actual
+}
+
+test_expect_success 'errors on missing commit argument' '
+	test_when_finished "rm -rf repo" &&
+	git init repo &&
+	(
+		cd repo &&
+		test_commit initial &&
+		test_must_fail git history fixup 2>err &&
+		test_grep "command expects a single revision" err
+	)
+'
+
+test_expect_success 'errors on too many arguments' '
+	test_when_finished "rm -rf repo" &&
+	git init repo &&
+	(
+		cd repo &&
+		test_commit initial &&
+		test_must_fail git history fixup HEAD HEAD 2>err &&
+		test_grep "command expects a single revision" err
+	)
+'
+
+test_expect_success 'errors on unknown revision' '
+	test_when_finished "rm -rf repo" &&
+	git init repo &&
+	(
+		cd repo &&
+		test_commit initial &&
+		test_must_fail git history fixup does-not-exist 2>err &&
+		test_grep "commit cannot be found: does-not-exist" err
+	)
+'
+
+test_expect_success 'errors when nothing is staged' '
+	test_when_finished "rm -rf repo" &&
+	git init repo &&
+	(
+		cd repo &&
+		test_commit initial &&
+		test_must_fail git history fixup HEAD 2>err &&
+		test_grep "nothing to fixup: no staged changes" err
+	)
+'
+
+test_expect_success 'errors in a bare repository' '
+	test_when_finished "rm -rf repo repo.git" &&
+	git init repo &&
+	test_commit -C repo initial &&
+	git clone --bare repo repo.git &&
+	test_must_fail git -C repo.git history fixup HEAD 2>err &&
+	test_grep "cannot run fixup in a bare repository" err
+'
+
+test_expect_success 'errors with invalid --empty= value' '
+	test_when_finished "rm -rf repo" &&
+	git init repo &&
+	test_must_fail git -C repo history fixup --empty=bogus HEAD 2>err &&
+	test_grep "unrecognized.*--empty.*bogus" err
+'
+
+test_expect_success 'can fixup the tip commit' '
+	test_when_finished "rm -rf repo" &&
+	git init repo &&
+	(
+		cd repo &&
+		test_commit initial &&
+		echo content >file.txt &&
+		git add file.txt &&
+		git commit -m "add file" &&
+
+		echo fix >>file.txt &&
+		git add file.txt &&
+
+		expect_changes <<-\EOF &&
+		add file
+		1	0	file.txt
+		initial
+		1	0	initial.t
+		EOF
+
+		git symbolic-ref HEAD >branch-expect &&
+		git history fixup HEAD &&
+		git symbolic-ref HEAD >branch-actual &&
+		test_cmp branch-expect branch-actual &&
+
+		expect_changes <<-\EOF &&
+		add file
+		2	0	file.txt
+		initial
+		1	0	initial.t
+		EOF
+
+		# Verify the fix is in the tip commit tree
+		git show HEAD:file.txt >actual &&
+		printf "content\nfix\n" >expect &&
+		test_cmp expect actual &&
+
+		git reflog >reflog &&
+		test_grep "fixup: updating HEAD" reflog
+	)
+'
+
+test_expect_success 'can fixup a commit in the middle of history' '
+	test_when_finished "rm -rf repo" &&
+	git init repo &&
+	(
+		cd repo &&
+		test_commit first &&
+		echo content >file.txt &&
+		git add file.txt &&
+		git commit -m "add file" &&
+		test_commit third &&
+
+		echo fix >>file.txt &&
+		git add file.txt &&
+
+		expect_changes <<-\EOF &&
+		third
+		1	0	third.t
+		add file
+		1	0	file.txt
+		first
+		1	0	first.t
+		EOF
+
+		git history fixup HEAD~ &&
+
+		expect_changes <<-\EOF &&
+		third
+		1	0	third.t
+		add file
+		2	0	file.txt
+		first
+		1	0	first.t
+		EOF
+
+		# Verify the fix landed in the "add file" commit.
+		git show HEAD~:file.txt >actual &&
+		printf "content\nfix\n" >expect &&
+		test_cmp expect actual &&
+
+		# And verify that the replayed commit also has the change.
+		git show HEAD:file.txt >actual &&
+		printf "content\nfix\n" >expect &&
+		test_cmp expect actual
+	)
+'
+
+test_expect_success 'can fixup root commit' '
+	test_when_finished "rm -rf repo" &&
+	git init repo &&
+	(
+		cd repo &&
+		echo initial >root.txt &&
+		git add root.txt &&
+		git commit -m "root" &&
+		test_commit second &&
+
+		expect_changes <<-\EOF &&
+		second
+		1	0	second.t
+		root
+		1	0	root.txt
+		EOF
+
+		echo fix >>root.txt &&
+		git add root.txt &&
+		git history fixup HEAD~ &&
+
+		expect_changes <<-\EOF &&
+		second
+		1	0	second.t
+		root
+		2	0	root.txt
+		EOF
+
+		git show HEAD~:root.txt >actual &&
+		printf "initial\nfix\n" >expect &&
+		test_cmp expect actual
+	)
+'
+
+test_expect_success 'preserves commit message and authorship' '
+	test_when_finished "rm -rf repo" &&
+	git init repo &&
+	(
+		cd repo &&
+		test_commit initial &&
+		echo content >file.txt &&
+		git add file.txt &&
+		git commit --author="Original <original@example.com>" -m "original message" &&
+
+		echo fix >>file.txt &&
+		git add file.txt &&
+		git history fixup HEAD &&
+
+		# Message preserved
+		git log -1 --format="%s" >actual &&
+		echo "original message" >expect &&
+		test_cmp expect actual &&
+
+		# Authorship preserved
+		git log -1 --format="%an <%ae>" >actual &&
+		echo "Original <original@example.com>" >expect &&
+		test_cmp expect actual
+	)
+'
+
+test_expect_success 'updates all descendant branches by default' '
+	test_when_finished "rm -rf repo" &&
+	git init repo --initial-branch=main &&
+	(
+		cd repo &&
+		test_commit base &&
+		git branch branch &&
+		test_commit ours &&
+		git switch branch &&
+		test_commit theirs &&
+		git switch main &&
+
+		echo fix >fix.txt &&
+		git add fix.txt &&
+		git history fixup base &&
+
+		expect_changes --branches <<-\EOF &&
+		theirs
+		1	0	theirs.t
+		ours
+		1	0	ours.t
+		base
+		1	0	base.t
+		1	0	fix.txt
+		EOF
+
+		# Both branches should have the fix in the base
+		git show main~:fix.txt >actual &&
+		echo fix >expect &&
+		test_cmp expect actual &&
+		git show branch~:fix.txt >actual &&
+		test_cmp expect actual
+	)
+'
+
+test_expect_success 'can fixup commit on a different branch' '
+	test_when_finished "rm -rf repo" &&
+	git init repo &&
+	(
+		cd repo &&
+		test_commit base &&
+		git branch theirs &&
+		test_commit ours &&
+		git switch theirs &&
+		test_commit theirs &&
+
+		# Stage a change while on "theirs"
+		echo fix >fix.txt &&
+		git add fix.txt &&
+
+		# Ensure that "ours" does not change, as it does not contain
+		# the commit in question.
+		git rev-parse ours >ours-before &&
+		git history fixup theirs &&
+		git rev-parse ours >ours-after &&
+		test_cmp ours-before ours-after &&
+
+		git show HEAD:fix.txt >actual &&
+		echo fix >expect &&
+		test_cmp expect actual
+	)
+'
+
+test_expect_success '--dry-run prints ref updates without modifying repo' '
+	test_when_finished "rm -rf repo" &&
+	git init repo --initial-branch=main &&
+	(
+		cd repo &&
+		test_commit base &&
+		git branch branch &&
+		test_commit main-tip &&
+		git switch branch &&
+		test_commit branch-tip &&
+		git switch main &&
+
+		echo fix >fix.txt &&
+		git add fix.txt &&
+
+		git refs list >refs-before &&
+		git history fixup --dry-run base >updates &&
+		git refs list >refs-after &&
+		test_cmp refs-before refs-after &&
+
+		test_grep "update refs/heads/main" updates &&
+		test_grep "update refs/heads/branch" updates &&
+
+		expect_changes --branches <<-\EOF &&
+		branch-tip
+		1	0	branch-tip.t
+		main-tip
+		1	0	main-tip.t
+		base
+		1	0	base.t
+		EOF
+
+		git update-ref --stdin <updates &&
+		expect_changes --branches <<-\EOF
+		branch-tip
+		1	0	branch-tip.t
+		main-tip
+		1	0	main-tip.t
+		base
+		1	0	base.t
+		1	0	fix.txt
+		EOF
+	)
+'
+
+test_expect_success '--update-refs=head updates only HEAD' '
+	test_when_finished "rm -rf repo" &&
+	git init repo --initial-branch=main &&
+	(
+		cd repo &&
+		test_commit base &&
+		git branch branch &&
+		test_commit main-tip &&
+		git switch branch &&
+		test_commit branch-tip &&
+
+		echo fix >fix.txt &&
+		git add fix.txt &&
+
+		# Only HEAD (branch) should be updated
+		git history fixup --update-refs=head base &&
+
+		# The main branch should be unaffected.
+		expect_changes main <<-\EOF &&
+		main-tip
+		1	0	main-tip.t
+		base
+		1	0	base.t
+		EOF
+
+		# But the currently checked out branch should be modified.
+		expect_changes branch <<-\EOF
+		branch-tip
+		1	0	branch-tip.t
+		base
+		1	0	base.t
+		1	0	fix.txt
+		EOF
+	)
+'
+
+test_expect_success '--update-refs=head refuses to rewrite commits not in HEAD ancestry' '
+	test_when_finished "rm -rf repo" &&
+	git init repo --initial-branch=main &&
+	(
+		cd repo &&
+		test_commit base &&
+		git branch other &&
+		test_commit main-tip &&
+		git switch other &&
+		test_commit other-tip &&
+
+		echo fix >fix.txt &&
+		git add fix.txt &&
+
+		test_must_fail git history fixup --update-refs=head main-tip 2>err &&
+		test_grep "rewritten commit must be an ancestor of HEAD" err
+	)
+'
+
+test_expect_success 'aborts when fixup would produce conflicts' '
+	test_when_finished "rm -rf repo" &&
+	git init repo &&
+	(
+		cd repo &&
+
+		echo "line one" >file.txt &&
+		git add file.txt &&
+		git commit -m "first" &&
+
+		echo "line two" >file.txt &&
+		git add file.txt &&
+		git commit -m "second" &&
+
+		echo "conflicting change" >file.txt &&
+		git add file.txt &&
+
+		git refs list >refs-before &&
+		test_must_fail git history fixup HEAD~ 2>err &&
+		test_grep "fixup would produce conflicts" err &&
+		git refs list >refs-after &&
+		test_cmp refs-before refs-after
+	)
+'
+
+test_expect_success '--reedit-message opens editor for the commit message' '
+	test_when_finished "rm -rf repo" &&
+	git init repo &&
+	(
+		cd repo &&
+		test_commit initial &&
+		echo content >file.txt &&
+		git add file.txt &&
+		git commit -m "add file" &&
+
+		echo fix >>file.txt &&
+		git add file.txt &&
+
+		fixup_with_message HEAD <<-\EOF &&
+		add file with fix
+		EOF
+
+		expect_changes --branches <<-\EOF
+		add file with fix
+		2	0	file.txt
+		initial
+		1	0	initial.t
+		EOF
+	)
+'
+
+test_expect_success 'retains unstaged working tree changes after fixup' '
+	test_when_finished "rm -rf repo" &&
+	git init repo &&
+	(
+		cd repo &&
+		touch a b &&
+		git add . &&
+		git commit -m "initial commit" &&
+		echo staged >a &&
+		echo unstaged >b &&
+		git add a &&
+		git history fixup HEAD &&
+
+		# b is still modified in the worktree but not staged
+		cat >expect <<-\EOF &&
+		 M b
+		EOF
+		git status --porcelain --untracked-files=no >actual &&
+		test_cmp expect actual
+	)
+'
+
+test_expect_success 'index is clean after fixup when target is HEAD' '
+	test_when_finished "rm -rf repo" &&
+	git init repo &&
+	(
+		cd repo &&
+
+		test_commit initial &&
+		echo fix >fix.txt &&
+		git add fix.txt &&
+		git history fixup HEAD &&
+
+		git status --porcelain --untracked-files=no >actual &&
+		test_must_be_empty actual
+	)
+'
+
+test_expect_success 'index is unchanged on conflict' '
+	test_when_finished "rm -rf repo" &&
+	git init repo &&
+	(
+		cd repo &&
+
+		echo base >file.txt &&
+		git add file.txt &&
+		git commit -m base &&
+		echo change >file.txt &&
+		git add file.txt &&
+		git commit -m change &&
+
+		echo conflict >file.txt &&
+		git add file.txt &&
+
+		git diff --cached >index-before &&
+		test_must_fail git history fixup HEAD~ &&
+		git diff --cached >index-after &&
+		test_cmp index-before index-after
+	)
+'
+
+test_expect_success '--empty=drop removes target commit and replays descendants onto its parent' '
+	test_when_finished "rm -rf repo" &&
+	git init repo --initial-branch=main &&
+	(
+		cd repo &&
+
+		test_commit first &&
+		test_commit second &&
+		test_commit third &&
+
+		git rm second.t &&
+		git history fixup --empty=drop HEAD~ &&
+
+		expect_changes <<-\EOF &&
+		third
+		1	0	third.t
+		first
+		1	0	first.t
+		EOF
+		test_must_fail git show HEAD:second.t
+	)
+'
+
+test_expect_success '--empty=drop errors out when dropping the root commit' '
+	test_when_finished "rm -rf repo" &&
+	git init repo &&
+	(
+		cd repo &&
+
+		test_commit first &&
+		test_commit second &&
+
+		git rm first.t &&
+		test_must_fail git history fixup --empty=drop HEAD~ 2>err &&
+		test_grep "cannot drop root commit" err
+	)
+'
+
+test_expect_success '--empty=drop can drop the HEAD commit' '
+	test_when_finished "rm -rf repo" &&
+	git init repo &&
+	(
+		cd repo &&
+
+		test_commit first &&
+		test_commit second &&
+
+		git rm second.t &&
+		git history fixup --empty=drop HEAD &&
+
+		expect_changes <<-\EOF
+		first
+		1	0	first.t
+		EOF
+	)
+'
+
+test_expect_success '--empty=drop drops empty replayed commits' '
+	test_when_finished "rm -rf repo" &&
+	git init repo &&
+	(
+		cd repo &&
+
+		touch base remove-me &&
+		git add . &&
+		git commit -m "base" &&
+		git rm remove-me &&
+		git commit -m "remove" &&
+		touch reintroduce remove-me &&
+		git add . &&
+		git commit -m "reintroduce" &&
+
+		git rm remove-me &&
+		git history fixup --empty=drop HEAD~2 &&
+
+		expect_changes <<-\EOF
+		reintroduce
+		0	0	reintroduce
+		0	0	remove-me
+		base
+		0	0	base
+		EOF
+	)
+'
+
+test_expect_success '--empty=keep keeps commit when fixup target becomes empty' '
+	test_when_finished "rm -rf repo" &&
+	git init repo &&
+	(
+		cd repo &&
+
+		test_commit first &&
+		test_commit second &&
+		test_commit third &&
+
+		git rm second.t &&
+		git history fixup --empty=keep HEAD~ &&
+
+		expect_changes <<-\EOF
+		third
+		1	0	third.t
+		second
+		first
+		1	0	first.t
+		EOF
+	)
+'
+
+test_expect_success '--empty=keep keeps commit when replayed commit becomes empty' '
+	test_when_finished "rm -rf repo" &&
+	git init repo &&
+	(
+		cd repo &&
+
+		touch base remove-me &&
+		git add . &&
+		git commit -m "base" &&
+		git rm remove-me &&
+		git commit -m "remove" &&
+		touch reintroduce remove-me &&
+		git add . &&
+		git commit -m "reintroduce" &&
+
+		git rm remove-me &&
+		git history fixup --empty=keep HEAD~2 &&
+
+		expect_changes <<-\EOF
+		reintroduce
+		0	0	reintroduce
+		0	0	remove-me
+		remove
+		base
+		0	0	base
+		EOF
+	)
+'
+
+test_expect_success '--empty=abort errors out when fixup target becomes empty' '
+	test_when_finished "rm -rf repo" &&
+	git init repo &&
+	(
+		cd repo &&
+
+		test_commit first &&
+		test_commit second &&
+
+		git rm first.t &&
+		test_must_fail git history fixup --empty=abort HEAD~ 2>err &&
+		test_grep "fixup makes commit.*empty" err
+	)
+'
+
+test_expect_success '--empty=abort errors out when a descendant becomes empty during replay' '
+	test_when_finished "rm -rf repo" &&
+	git init repo --initial-branch=main &&
+	(
+		cd repo &&
+
+		touch base remove-me &&
+		git add . &&
+		git commit -m "base" &&
+		git rm remove-me &&
+		git commit -m "remove" &&
+		touch reintroduce remove-me &&
+		git add . &&
+		git commit -m "reintroduce" &&
+
+		git rm remove-me &&
+		test_must_fail git history fixup --empty=abort HEAD~2 2>err &&
+		test_grep "became empty after replay" err
+	)
+'
+
+test_done
diff --git a/t/t3500-cherry.sh b/t/t3500-cherry.sh
index 78c3eac..3e66827 100755
--- a/t/t3500-cherry.sh
+++ b/t/t3500-cherry.sh
@@ -78,4 +78,31 @@
 	test_cmp expect actual
 '
 
+# Reuse the expect file from the previous test, in a partial clone
+test_expect_success 'cherry in partial clone does bulk prefetch' '
+	test_config uploadpack.allowfilter 1 &&
+	test_config uploadpack.allowanysha1inwant 1 &&
+	test_when_finished "rm -rf copy" &&
+
+	git clone --bare --filter=blob:none file://"$(pwd)" copy &&
+	(
+		cd copy &&
+		GIT_TRACE2_EVENT="$(pwd)/trace.output" git cherry upstream-with-space feature-without-space >actual &&
+		test_cmp ../expect actual &&
+
+		grep "child_start.*fetch.negotiationAlgorithm" trace.output >fetches &&
+		test_line_count = 1 fetches &&
+		test_trace2_data promisor fetch_count 4 <trace.output &&
+
+		# A second invocation should not refetch any blobs, since
+		# the prefetch is expected to filter out OIDs that are
+		# already present locally.
+		GIT_TRACE2_EVENT="$(pwd)/trace2.output" git cherry upstream-with-space feature-without-space >actual &&
+		test_cmp ../expect actual &&
+
+		! grep "child_start.*fetch.negotiationAlgorithm" trace2.output &&
+		! grep "\"key\":\"fetch_count\"" trace2.output
+	)
+'
+
 test_done
diff --git a/t/t3600-rm.sh b/t/t3600-rm.sh
index 1f16e6b..a371ea6 100755
--- a/t/t3600-rm.sh
+++ b/t/t3600-rm.sh
@@ -260,7 +260,7 @@
 
 test_expect_success !MINGW 'choking "git rm" should not let it die with cruft (induce and check SIGPIPE)' '
 	choke_git_rm_setup &&
-	OUT=$( ((trap "" PIPE && git rm -n "some-file-*"; echo $? 1>&3) | :) 3>&1 ) &&
+	OUT=$( ((trap "" PIPE && git rm -n "some-file-*" && echo 0 1>&3 || echo $? 1>&3) | :) 3>&1 ) &&
 	test_match_signal 13 "$OUT" &&
 	test_path_is_missing .git/index.lock
 '
diff --git a/t/t3650-replay-basics.sh b/t/t3650-replay-basics.sh
index 217f6fb..3353bc4 100755
--- a/t/t3650-replay-basics.sh
+++ b/t/t3650-replay-basics.sh
@@ -81,9 +81,13 @@
 	test_cmp expect actual
 '
 
-test_expect_success 'no base or negative ref gives no-replaying down to root error' '
-	echo "fatal: replaying down from root commit is not supported yet!" >expect &&
-	test_must_fail git replay --onto=topic1 topic2 2>actual &&
+test_expect_success 'replay down to root onto another branch' '
+	git replay --ref-action=print --onto main topic2 >result &&
+
+	test_line_count = 1 result &&
+
+	git log --format=%s $(cut -f 3 -d " " result) >actual &&
+	test_write_lines E D C M L B A >expect &&
 	test_cmp expect actual
 '
 
@@ -495,4 +499,70 @@
 	test_grep "cannot be used together" error
 '
 
+test_expect_success 'using --onto with --ref' '
+	git branch test-ref-onto topic2 &&
+	test_when_finished "git branch -D test-ref-onto" &&
+
+	git replay --ref-action=print --onto=main --ref=refs/heads/test-ref-onto topic1..topic2 >result &&
+
+	test_line_count = 1 result &&
+	test_grep "^update refs/heads/test-ref-onto " result &&
+
+	git log --format=%s $(cut -f 3 -d " " result) >actual &&
+	test_write_lines E D M L B A >expect &&
+	test_cmp expect actual
+'
+
+test_expect_success 'using --advance with --ref' '
+	git branch test-ref-advance main &&
+	git branch test-ref-target main &&
+	test_when_finished "git branch -D test-ref-advance test-ref-target" &&
+
+	git replay --ref-action=print --advance=test-ref-advance --ref=refs/heads/test-ref-target topic1..topic2 >result &&
+
+	test_line_count = 1 result &&
+	test_grep "^update refs/heads/test-ref-target " result
+'
+
+test_expect_success 'using --revert with --ref' '
+	git branch test-ref-revert topic4 &&
+	git branch test-ref-revert-target topic4 &&
+	test_when_finished "git branch -D test-ref-revert test-ref-revert-target" &&
+
+	git replay --ref-action=print --revert=test-ref-revert --ref=refs/heads/test-ref-revert-target topic4~1..topic4 >result &&
+
+	test_line_count = 1 result &&
+	test_grep "^update refs/heads/test-ref-revert-target " result
+'
+
+test_expect_success '--ref is incompatible with --contained' '
+	test_must_fail git replay --onto=main --ref=refs/heads/main --contained topic1..topic2 2>err &&
+	test_grep "cannot be used together" err
+'
+
+test_expect_success '--ref with nonexistent fully-qualified ref' '
+	test_when_finished "git update-ref -d refs/heads/new-branch" &&
+
+	git replay --onto=main --ref=refs/heads/new-branch topic1..topic2 &&
+
+	git log --format=%s -2 new-branch >actual &&
+	test_write_lines E D >expect &&
+	test_cmp expect actual
+'
+
+test_expect_success '--ref must be a valid refname' '
+	test_must_fail git replay --onto=main --ref="refs/heads/bad..ref" topic1..topic2 2>err &&
+	test_grep "is not a valid refname" err
+'
+
+test_expect_success '--ref requires fully qualified ref' '
+	test_must_fail git replay --onto=main --ref=main topic1..topic2 2>err &&
+	test_grep "is not a valid refname" err
+'
+
+test_expect_success '--onto with --ref rejects multiple revision ranges' '
+	test_must_fail git replay --onto=main --ref=refs/heads/topic2 ^topic1 topic2 topic4 2>err &&
+	test_grep "cannot be used with multiple revision ranges" err
+'
+
 test_done
diff --git a/t/t3901-i18n-patch.sh b/t/t3901-i18n-patch.sh
index f03601b..ef7d7e1 100755
--- a/t/t3901-i18n-patch.sh
+++ b/t/t3901-i18n-patch.sh
@@ -28,7 +28,8 @@
 		8859)
 			grep "^encoding ISO8859-1" ;;
 		*)
-			grep "^encoding ISO8859-1"; test "$?" != 0 ;;
+			ret=0; grep "^encoding ISO8859-1" || ret=$?
+			test "$ret" != 0 ;;
 		esac || return 1
 		j=$i
 		i=$(($i+1))
diff --git a/t/t3903-stash.sh b/t/t3903-stash.sh
index 7087994..ecc35aa 100755
--- a/t/t3903-stash.sh
+++ b/t/t3903-stash.sh
@@ -56,6 +56,7 @@
 	git add other-file &&
 	test_tick &&
 	git commit -m initial &&
+	git tag initial &&
 	echo 2 >file &&
 	git add file &&
 	echo 3 >file &&
@@ -1790,4 +1791,44 @@
 	test_cmp expect file
 '
 
+test_expect_success 'apply with custom conflict labels' '
+	git reset --hard initial &&
+	test_commit label-base conflict-file base-content &&
+	echo stashed >conflict-file &&
+	git stash push -m "stashed" &&
+	test_commit label-upstream conflict-file upstream-content &&
+	test_must_fail git -c merge.conflictStyle=diff3 stash apply --label-ours=UP --label-theirs=STASH &&
+	test_grep "^<<<<<<< UP" conflict-file &&
+	test_grep "^||||||| Stash base" conflict-file &&
+	test_grep "^>>>>>>> STASH" conflict-file
+'
+
+test_expect_success 'apply with empty conflict labels' '
+	git reset --hard initial &&
+	test_commit empty-label-base conflict-file base-content &&
+	echo stashed >conflict-file &&
+	git stash push -m "stashed" &&
+	test_commit empty-label-upstream conflict-file upstream-content &&
+	test_must_fail git stash apply --label-ours= --label-theirs= &&
+	test_grep "^<<<<<<<$" conflict-file &&
+	test_grep "^>>>>>>>$" conflict-file
+'
+
+test_expect_success 'stash show --include-untracked includes untracked files' '
+	git reset --hard &&
+
+	echo tracked >tracked &&
+	git add tracked &&
+	git commit -m "base" &&
+
+	echo change >>tracked &&
+	echo untracked >untracked &&
+
+	git stash push --include-untracked &&
+	test_path_is_missing untracked &&
+
+	git stash show --include-untracked >actual &&
+	test_grep "untracked" actual
+'
+
 test_done
diff --git a/t/t4018/scheme-lisp-defun-a b/t/t4018/scheme-lisp-defun-a
new file mode 100644
index 0000000..c3c750f
--- /dev/null
+++ b/t/t4018/scheme-lisp-defun-a
@@ -0,0 +1,4 @@
+(defun some-func (x y z) RIGHT
+  (let ((a x)
+        (b y))
+        (ChangeMe a b)))
diff --git a/t/t4018/scheme-lisp-defun-b b/t/t4018/scheme-lisp-defun-b
new file mode 100644
index 0000000..21be305
--- /dev/null
+++ b/t/t4018/scheme-lisp-defun-b
@@ -0,0 +1,4 @@
+(macrolet ((foo (x) `(bar ,x)))
+  (defun mumble (x) ; RIGHT
+    (when (> x 0)
+      (foo x)))) ; ChangeMe
diff --git a/t/t4018/scheme-lisp-eval-when b/t/t4018/scheme-lisp-eval-when
new file mode 100644
index 0000000..5d941d7
--- /dev/null
+++ b/t/t4018/scheme-lisp-eval-when
@@ -0,0 +1,4 @@
+(eval-when (:compile-toplevel :load-toplevel :execute)  ; RIGHT
+  (set-macro-character #\?
+		       (lambda (stream char)
+			 `(make-pattern-variable ,(read stream)))))  ; ChangeMe
diff --git a/t/t4018/scheme-module b/t/t4018/scheme-module-a
similarity index 100%
rename from t/t4018/scheme-module
rename to t/t4018/scheme-module-a
diff --git a/t/t4018/scheme-module-b b/t/t4018/scheme-module-b
new file mode 100644
index 0000000..77bc0c5
--- /dev/null
+++ b/t/t4018/scheme-module-b
@@ -0,0 +1,6 @@
+(module A
+  (export with-display-exception)
+  (extern (display-exception display-exception))
+  (def (with-display-exception thunk) RIGHT
+    (with-catch (lambda (e) (display-exception e (current-error-port)) e)
+      thunk ChangeMe)))
diff --git a/t/t4032-diff-inter-hunk-context.sh b/t/t4032-diff-inter-hunk-context.sh
index bada0cb..7d44396 100755
--- a/t/t4032-diff-inter-hunk-context.sh
+++ b/t/t4032-diff-inter-hunk-context.sh
@@ -17,7 +17,7 @@
 
 t() {
 	use_config=
-	git config --unset diff.interHunkContext
+	git config --unset diff.interHunkContext || :
 
 	case $# in
 	4) hunks=$4; cmd="diff -U$3";;
@@ -40,11 +40,13 @@
 		test $(git $cmd $file | grep '^@@ ' | wc -l) = $hunks
 	"
 
-	test -f $expected &&
-	test_expect_success "$label: check output" "
-		git $cmd $file | grep -v '^index ' >actual &&
-		test_cmp $expected actual
-	"
+	if test -f $expected
+	then
+		test_expect_success "$label: check output" "
+			git $cmd $file | grep -v '^index ' >actual &&
+			test_cmp $expected actual
+		"
+	fi
 }
 
 cat <<EOF >expected.f1.0.1 || exit 1
@@ -114,4 +116,10 @@
 	test_must_fail git diff
 '
 
+test_expect_success '--inter-hunk-context rejects negative value' '
+	test_unconfig diff.interHunkContext &&
+	test_must_fail git diff --inter-hunk-context=-1 2>err &&
+	test_grep "expects a non-negative integer" err
+'
+
 test_done
diff --git a/t/t4034/scheme/expect b/t/t4034/scheme/expect
index 496cd5d..fb7f261 100644
--- a/t/t4034/scheme/expect
+++ b/t/t4034/scheme/expect
@@ -2,10 +2,11 @@
 <BOLD>index 74b6605..63b6ac4 100644<RESET>
 <BOLD>--- a/pre<RESET>
 <BOLD>+++ b/post<RESET>
-<CYAN>@@ -1,6 +1,6 @@<RESET>
+<CYAN>@@ -1,7 +1,7 @@<RESET>
 (define (<RED>myfunc a b<RESET><GREEN>my-func first second<RESET>)
   ; This is a <RED>really<RESET><GREEN>(moderately)<RESET> cool function.
   (<RED>this\place<RESET><GREEN>that\place<RESET> (+ 3 4))
-  (define <RED>some-text<RESET><GREEN>|a greeting|<RESET> "hello")
+  (define <RED>|the \| \greeting|<RESET><GREEN>|a \greeting|<RESET> |hello there|)
+  ({<RED>}<RESET>(([<RED>]<RESET>(func-n)<RED>[<RESET>]))<RED>{<RESET>})
   (let ((c (<RED>+ a b<RESET><GREEN>add1 first<RESET>)))
     (format "one more than the total is %d" (<RED>add1<RESET><GREEN>+<RESET> c <GREEN>second<RESET>))))
diff --git a/t/t4034/scheme/post b/t/t4034/scheme/post
index 63b6ac4..450cc23 100644
--- a/t/t4034/scheme/post
+++ b/t/t4034/scheme/post
@@ -1,6 +1,7 @@
 (define (my-func first second)
   ; This is a (moderately) cool function.
   (that\place (+ 3 4))
-  (define |a greeting| "hello")
+  (define |a \greeting| |hello there|)
+  ({(([(func-n)]))})
   (let ((c (add1 first)))
     (format "one more than the total is %d" (+ c second))))
diff --git a/t/t4034/scheme/pre b/t/t4034/scheme/pre
index 74b6605..e16ee75 100644
--- a/t/t4034/scheme/pre
+++ b/t/t4034/scheme/pre
@@ -1,6 +1,7 @@
 (define (myfunc a b)
   ; This is a really cool function.
   (this\place (+ 3 4))
-  (define some-text "hello")
+  (define |the \| \greeting| |hello there|)
+  ({}(([](func-n)[])){})
   (let ((c (+ a b)))
     (format "one more than the total is %d" (add1 c))))
diff --git a/t/t4052-stat-output.sh b/t/t4052-stat-output.sh
index 7c74906..e009585 100755
--- a/t/t4052-stat-output.sh
+++ b/t/t4052-stat-output.sh
@@ -445,4 +445,29 @@
 	test_grep "<RED>|<RESET>  ${FILENAME_TRIMMED} | 0" out
 '
 
+test_expect_success 'diffstat truncation with invalid UTF-8 does not crash' '
+	empty_blob=$(git hash-object -w --stdin </dev/null) &&
+	printf "100644 blob $empty_blob\taaa-\300-aaa\n" |
+	git mktree >tree_file &&
+	tree=$(cat tree_file) &&
+	empty_tree=$(git mktree </dev/null) &&
+	c1=$(git commit-tree -m before $empty_tree) &&
+	c2=$(git commit-tree -m after -p $c1 $tree) &&
+	git -c core.quotepath=false diff --stat --stat-name-width=5 $c1..$c2 >output &&
+	test_grep "| 0" output
+'
+
+test_expect_success FUNNYNAMES 'diffstat truncation with control chars does not read out of bounds' '
+	FNAME=$(printf "aaa-\302\237\302\237\302\237-aaa") &&
+	git commit --allow-empty -m setup &&
+	>$FNAME &&
+	git add -- $FNAME &&
+	git commit -m "add file with control char name" &&
+	git -c core.quotepath=false diff --stat --stat-name-width=5 HEAD~1..HEAD >output &&
+	test_grep "| 0" output &&
+	rm -- $FNAME &&
+	git rm -- $FNAME &&
+	git commit -m "remove test file"
+'
+
 test_done
diff --git a/t/t4055-diff-context.sh b/t/t4055-diff-context.sh
index 1384a81..b26f6ee 100755
--- a/t/t4055-diff-context.sh
+++ b/t/t4055-diff-context.sh
@@ -82,6 +82,11 @@
 	test_grep "bad config variable" output
 '
 
+test_expect_success '-U-1 is rejected' '
+	test_must_fail git diff -U-1 2>err &&
+	test_grep "expects a non-negative integer" err
+'
+
 test_expect_success '-U0 is valid, so is diff.context=0' '
 	test_config diff.context 0 &&
 	git diff >output &&
diff --git a/t/t4124-apply-ws-rule.sh b/t/t4124-apply-ws-rule.sh
index 29ea7d4..205d86d 100755
--- a/t/t4124-apply-ws-rule.sh
+++ b/t/t4124-apply-ws-rule.sh
@@ -561,6 +561,22 @@
 	git config core.whitespace incomplete-line
 '
 
+test_expect_success 'no incomplete context line (not an error)' '
+	test_when_finished "rm -f sample*-i patch patch-new target" &&
+	test_write_lines 1 2 3 "" 4 5 >sample-i &&
+	test_write_lines 1 2 3 "" 0 5 >sample2-i &&
+	cat sample-i >target &&
+	git add target &&
+	cat sample2-i >target &&
+	git diff-files -p target >patch &&
+	sed -e "s/^ $//" <patch >patch-new &&
+
+	cat sample-i >target &&
+	git apply --whitespace=fix <patch-new 2>error &&
+	test_cmp sample2-i target &&
+	test_must_be_empty error
+'
+
 test_expect_success 'incomplete context line (not an error)' '
 	(test_write_lines 1 2 3 4 5 && printf 6) >sample-i &&
 	(test_write_lines 1 2 3 0 5 && printf 6) >sample2-i &&
diff --git a/t/t4203-mailmap.sh b/t/t4203-mailmap.sh
index 74b7ddc..249548e 100755
--- a/t/t4203-mailmap.sh
+++ b/t/t4203-mailmap.sh
@@ -1133,6 +1133,111 @@
 	test_cmp expect actual
 '
 
+test_expect_success 'git cat-file --batch-command mailmap yes enables mailmap mid-stream' '
+	test_when_finished "rm .mailmap" &&
+	cat >.mailmap <<-\EOF &&
+	C O Mitter <committer@example.com> Orig <orig@example.com>
+	EOF
+	commit_sha=$(git rev-parse HEAD) &&
+	git cat-file commit HEAD >commit_no_mailmap.out &&
+	git cat-file --use-mailmap commit HEAD >commit_mailmap.out &&
+	size_no_mailmap=$(wc -c <commit_no_mailmap.out) &&
+	size_mailmap=$(wc -c <commit_mailmap.out) &&
+	printf "info HEAD\nmailmap yes\ninfo HEAD\n" | git cat-file --batch-command >actual &&
+	echo $commit_sha commit $size_no_mailmap >expect &&
+	echo $commit_sha commit $size_mailmap >>expect &&
+	test_cmp expect actual
+'
+
+test_expect_success 'git cat-file --batch-command mailmap no disables mailmap mid-stream' '
+	test_when_finished "rm .mailmap" &&
+	cat >.mailmap <<-\EOF &&
+	C O Mitter <committer@example.com> Orig <orig@example.com>
+	EOF
+	commit_sha=$(git rev-parse HEAD) &&
+	git cat-file commit HEAD >commit_no_mailmap.out &&
+	git cat-file --use-mailmap commit HEAD >commit_mailmap.out &&
+	size_no_mailmap=$(wc -c <commit_no_mailmap.out) &&
+	size_mailmap=$(wc -c <commit_mailmap.out) &&
+	printf "mailmap yes\ninfo HEAD\nmailmap no\ninfo HEAD\n" | git cat-file --batch-command >actual &&
+	echo $commit_sha commit $size_mailmap >expect &&
+	echo $commit_sha commit $size_no_mailmap >>expect &&
+	test_cmp expect actual
+'
+
+test_expect_success 'git cat-file --batch-command mailmap works in --buffer mode' '
+	test_when_finished "rm .mailmap" &&
+	cat >.mailmap <<-\EOF &&
+	C O Mitter <committer@example.com> Orig <orig@example.com>
+	EOF
+	commit_sha=$(git rev-parse HEAD) &&
+	git cat-file commit HEAD >commit_no_mailmap.out &&
+	git cat-file --use-mailmap commit HEAD >commit_mailmap.out &&
+	size_no_mailmap=$(wc -c <commit_no_mailmap.out) &&
+	size_mailmap=$(wc -c <commit_mailmap.out) &&
+	printf "mailmap yes\ninfo HEAD\nmailmap no\ninfo HEAD\nflush\n" | git cat-file --batch-command --buffer >actual &&
+	echo $commit_sha commit $size_mailmap >expect &&
+	echo $commit_sha commit $size_no_mailmap >>expect &&
+	test_cmp expect actual
+'
+
+test_expect_success 'git cat-file --batch-command mailmap no overrides startup --mailmap' '
+	test_when_finished "rm .mailmap" &&
+	cat >.mailmap <<-\EOF &&
+	C O Mitter <committer@example.com> Orig <orig@example.com>
+	EOF
+	commit_sha=$(git rev-parse HEAD) &&
+	git cat-file --use-mailmap commit HEAD >commit_mailmap.out &&
+	size_mailmap=$(wc -c <commit_mailmap.out) &&
+	git cat-file commit HEAD >commit_no_mailmap.out &&
+	size_no_mailmap=$(wc -c <commit_no_mailmap.out) &&
+	printf "info HEAD\nmailmap no\ninfo HEAD\n" | \
+		git cat-file --mailmap --batch-command >actual &&
+	echo $commit_sha commit $size_mailmap >expect &&
+	echo $commit_sha commit $size_no_mailmap >>expect &&
+	test_cmp expect actual
+'
+
+test_expect_success 'git cat-file --batch-command mailmap yes overrides startup --no-mailmap' '
+	test_when_finished "rm .mailmap" &&
+	cat >.mailmap <<-\EOF &&
+	C O Mitter <committer@example.com> Orig <orig@example.com>
+	EOF
+	commit_sha=$(git rev-parse HEAD) &&
+	git cat-file commit HEAD >commit_no_mailmap.out &&
+	size_no_mailmap=$(wc -c <commit_no_mailmap.out) &&
+	git cat-file --use-mailmap commit HEAD >commit_mailmap.out &&
+	size_mailmap=$(wc -c <commit_mailmap.out) &&
+	printf "info HEAD\nmailmap yes\ninfo HEAD\n" | \
+		git cat-file --no-mailmap --batch-command >actual &&
+	echo $commit_sha commit $size_no_mailmap >expect &&
+	echo $commit_sha commit $size_mailmap >>expect &&
+	test_cmp expect actual
+'
+
+test_expect_success 'git cat-file --batch-command mailmap accepts true/false' '
+	test_when_finished "rm .mailmap" &&
+	cat >.mailmap <<-\EOF &&
+	C O Mitter <committer@example.com> Orig <orig@example.com>
+	EOF
+	commit_sha=$(git rev-parse HEAD) &&
+	git cat-file commit HEAD >commit_no_mailmap.out &&
+	size_no_mailmap=$(wc -c <commit_no_mailmap.out) &&
+	git cat-file --use-mailmap commit HEAD >commit_mailmap.out &&
+	size_mailmap=$(wc -c <commit_mailmap.out) &&
+	printf "mailmap true\ninfo HEAD\nmailmap false\ninfo HEAD\n" | \
+		git cat-file --batch-command >actual &&
+	echo $commit_sha commit $size_mailmap >expect &&
+	echo $commit_sha commit $size_no_mailmap >>expect &&
+	test_cmp expect actual
+'
+
+test_expect_success 'git cat-file --batch-command mailmap rejects invalid boolean' '
+	echo "mailmap maybe" >in &&
+	test_must_fail git cat-file --batch-command <in 2>err &&
+	test_grep "mailmap: invalid boolean .*maybe" err
+'
+
 test_expect_success 'git cat-file --mailmap works with different author and committer' '
 	test_when_finished "rm .mailmap" &&
 	cat >.mailmap <<-\EOF &&
diff --git a/t/t4211-line-log.sh b/t/t4211-line-log.sh
index 0a7c3ca..aaf197d 100755
--- a/t/t4211-line-log.sh
+++ b/t/t4211-line-log.sh
@@ -129,7 +129,7 @@
 	git checkout parallel-change &&
 	git log --output=log -L :main:b.c >output &&
 	test_must_be_empty output &&
-	test_line_count = 70 log
+	test_line_count = 75 log
 '
 
 test_expect_success 'range_set_union' '
@@ -339,14 +339,106 @@
 	test_cmp expect actual
 '
 
+test_expect_success 'setup for diff pipeline tests' '
+	git checkout parent-oids &&
+
+	head_blob_old=$(git rev-parse --short HEAD^:file.c) &&
+	head_blob_new=$(git rev-parse --short HEAD:file.c) &&
+	root_blob=$(git rev-parse --short HEAD~4:file.c) &&
+	null_blob=$(test_oid zero | cut -c1-7) &&
+	head_blob_old_full=$(git rev-parse HEAD^:file.c) &&
+	head_blob_new_full=$(git rev-parse HEAD:file.c) &&
+	root_blob_full=$(git rev-parse HEAD~4:file.c) &&
+	null_blob_full=$(test_oid zero)
+'
+
+test_expect_success '-L diff output includes index and new file mode' '
+	git log -L:func2:file.c --format= >actual &&
+
+	# Output should contain index headers (not present in old code path)
+	grep "^index $head_blob_old\.\.$head_blob_new 100644" actual &&
+
+	# Root commit should show new file mode and null index
+	grep "^new file mode 100644" actual &&
+	grep "^index $null_blob\.\.$root_blob$" actual &&
+
+	# Hunk headers should include funcname context
+	grep "^@@ .* @@ int func1()" actual
+'
+
+test_expect_success '-L with --word-diff' '
+	cat >expect <<-\EOF &&
+
+	diff --git a/file.c b/file.c
+	--- a/file.c
+	+++ b/file.c
+	@@ -6,4 +6,4 @@ int func1()
+	int func2()
+	{
+	    return [-F2;-]{+F2 + 2;+}
+	}
+
+	diff --git a/file.c b/file.c
+	new file mode 100644
+	--- /dev/null
+	+++ b/file.c
+	@@ -0,0 +6,4 @@
+	{+int func2()+}
+	{+{+}
+	{+    return F2;+}
+	{+}+}
+	EOF
+	git log -L:func2:file.c --word-diff --format= >actual &&
+	grep -v "^index " actual >actual.filtered &&
+	grep -v "^index " expect >expect.filtered &&
+	test_cmp expect.filtered actual.filtered
+'
+
+test_expect_success '-L with --no-prefix' '
+	git log -L:func2:file.c --no-prefix --format= >actual &&
+	grep "^diff --git file.c file.c" actual &&
+	grep "^--- file.c" actual &&
+	! grep "^--- a/" actual
+'
+
+test_expect_success '-L with --full-index' '
+	git log -L:func2:file.c --full-index --format= >actual &&
+	grep "^index $head_blob_old_full\.\.$head_blob_new_full 100644" actual &&
+	grep "^index $null_blob_full\.\.$root_blob_full$" actual
+'
+
+test_expect_success 'setup -L with whitespace change' '
+	git checkout -b ws-change parent-oids &&
+	sed "s/    return F2 + 2;/	return F2 + 2;/" file.c >tmp &&
+	mv tmp file.c &&
+	git commit -a -m "Whitespace change in func2()"
+'
+
+test_expect_success '-L with --ignore-all-space suppresses whitespace-only diff' '
+	git log -L:func2:file.c --format= >without_w &&
+	git log -L:func2:file.c --format= -w >with_w &&
+
+	# Without -w: three commits produce diffs (whitespace, modify, root)
+	test $(grep -c "^diff --git" without_w) = 3 &&
+
+	# With -w: whitespace-only commit produces no hunk, so only two diffs
+	test $(grep -c "^diff --git" with_w) = 2
+'
+
 test_expect_success 'show line-log with graph' '
+	git checkout parent-oids &&
+	head_blob_old=$(git rev-parse --short HEAD^:file.c) &&
+	head_blob_new=$(git rev-parse --short HEAD:file.c) &&
+	root_blob=$(git rev-parse --short HEAD~4:file.c) &&
+	null_blob=$(test_oid zero | cut -c1-7) &&
 	qz_to_tab_space >expect <<-EOF &&
 	* $head_oid Modify func2() in file.c
 	|Z
 	| diff --git a/file.c b/file.c
+	| index $head_blob_old..$head_blob_new 100644
 	| --- a/file.c
 	| +++ b/file.c
-	| @@ -6,4 +6,4 @@
+	| @@ -6,4 +6,4 @@ int func1()
 	|  int func2()
 	|  {
 	| -    return F2;
@@ -355,6 +447,8 @@
 	* $root_oid Add func1() and func2() in file.c
 	ZZ
 	  diff --git a/file.c b/file.c
+	  new file mode 100644
+	  index $null_blob..$root_blob
 	  --- /dev/null
 	  +++ b/file.c
 	  @@ -0,0 +6,4 @@
@@ -367,4 +461,254 @@
 	test_cmp expect actual
 '
 
+test_expect_success 'setup for -L with -G/-S/--find-object and a merge with rename' '
+	git checkout --orphan pickaxe-rename &&
+	git reset --hard &&
+
+	echo content >file &&
+	git add file &&
+	git commit -m "add file" &&
+
+	git checkout -b pickaxe-rename-side &&
+	git mv file renamed-file &&
+	git commit -m "rename file" &&
+
+	git checkout pickaxe-rename &&
+	git commit --allow-empty -m "diverge" &&
+	git merge --no-edit pickaxe-rename-side &&
+
+	git mv renamed-file file &&
+	git commit -m "rename back"
+'
+
+test_expect_success '-L -G does not crash with merge and rename' '
+	git log --format="%s" --no-patch -L 1,1:file -G "." >actual
+'
+
+test_expect_success '-L -S does not crash with merge and rename' '
+	git log --format="%s" --no-patch -L 1,1:file -S content >actual
+'
+
+test_expect_success '-L --find-object does not crash with merge and rename' '
+	git log --format="%s" --no-patch -L 1,1:file \
+		--find-object=$(git rev-parse HEAD:file) >actual
+'
+
+# Commit-level filtering with pickaxe does not yet work for -L.
+# show_log() prints the commit header before diffcore_std() runs
+# pickaxe, so commits cannot be suppressed even when no diff pairs
+# survive filtering.  Fixing this would require deferring show_log()
+# until after diffcore_std(), which is a larger restructuring of the
+# log-tree output pipeline.
+test_expect_failure '-L -G should filter commits by pattern' '
+	git log --format="%s" --no-patch -L 1,1:file -G "nomatch" >actual &&
+	test_must_be_empty actual
+'
+
+test_expect_failure '-L -S should filter commits by pattern' '
+	git log --format="%s" --no-patch -L 1,1:file -S "nomatch" >actual &&
+	test_must_be_empty actual
+'
+
+test_expect_failure '-L --find-object should filter commits by object' '
+	git log --format="%s" --no-patch -L 1,1:file \
+		--find-object=$ZERO_OID >actual &&
+	test_must_be_empty actual
+'
+
+test_expect_success '-L with --word-diff-regex' '
+	git checkout parent-oids &&
+	git log -L:func2:file.c --word-diff \
+		--word-diff-regex="[a-zA-Z0-9_]+" --format= >actual &&
+	# Word-diff markers must be present
+	grep "{+" actual &&
+	grep "+}" actual &&
+	# No line-level +/- markers (word-diff replaces them);
+	# exclude --- header lines from the check
+	! grep "^+[^+]" actual &&
+	! grep "^-[^-]" actual
+'
+
+test_expect_success '-L with --src-prefix and --dst-prefix' '
+	git checkout parent-oids &&
+	git log -L:func2:file.c --src-prefix=old/ --dst-prefix=new/ \
+		--format= >actual &&
+	grep "^diff --git old/file.c new/file.c" actual &&
+	grep "^--- old/file.c" actual &&
+	grep "^+++ new/file.c" actual &&
+	! grep "^--- a/" actual
+'
+
+test_expect_success '-L with --abbrev' '
+	git checkout parent-oids &&
+	git log -L:func2:file.c --abbrev=4 --format= -1 >actual &&
+	# 4-char abbreviated hashes on index line
+	grep "^index [0-9a-f]\{4\}\.\.[0-9a-f]\{4\}" actual
+'
+
+test_expect_success '-L with -b suppresses whitespace-only diff' '
+	git checkout ws-change &&
+	git log -L:func2:file.c --format= >without_b &&
+	git log -L:func2:file.c --format= -b >with_b &&
+	test $(grep -c "^diff --git" without_b) = 3 &&
+	test $(grep -c "^diff --git" with_b) = 2
+'
+
+test_expect_success '-L with --output-indicator-*' '
+	git checkout parent-oids &&
+	git log -L:func2:file.c --output-indicator-new=">" \
+		--output-indicator-old="<" --output-indicator-context="|" \
+		--format= -1 >actual &&
+	grep "^>" actual &&
+	grep "^<" actual &&
+	grep "^|" actual &&
+	# No standard +/-/space content markers; exclude ---/+++ headers
+	! grep "^+[^+]" actual &&
+	! grep "^-[^-]" actual &&
+	! grep "^ " actual
+'
+
+test_expect_success '-L with -R reverses diff' '
+	git checkout parent-oids &&
+	git log -L:func2:file.c -R --format= -1 >actual &&
+	grep "^diff --git b/file.c a/file.c" actual &&
+	grep "^--- b/file.c" actual &&
+	grep "^+++ a/file.c" actual &&
+	# The modification added "F2 + 2", so reversed it is removed
+	grep "^-.*F2 + 2" actual &&
+	grep "^+.*return F2;" actual
+'
+
+test_expect_success 'setup for color-moved test' '
+	git checkout -b color-moved-test parent-oids &&
+	cat >big.c <<-\EOF &&
+	int bigfunc()
+	{
+	    int a = 1;
+	    int b = 2;
+	    int c = 3;
+	    return a + b + c;
+	}
+	EOF
+	git add big.c &&
+	git commit -m "add bigfunc" &&
+	sed "s/    /	/" big.c >tmp && mv tmp big.c &&
+	git commit -a -m "reindent bigfunc"
+'
+
+test_expect_success '-L with --color-moved' '
+	git log -L:bigfunc:big.c --color-moved=zebra \
+		--color-moved-ws=ignore-all-space \
+		--color=always --format= -1 >actual.raw &&
+	test_decode_color <actual.raw >actual &&
+	# Old moved lines: bold magenta; new moved lines: bold cyan
+	grep "BOLD;MAGENTA" actual &&
+	grep "BOLD;CYAN" actual
+'
+
+test_expect_success 'setup for no-newline-at-eof tests' '
+	git checkout --orphan no-newline &&
+	git reset --hard &&
+	printf "int top()\n{\n    return 1;\n}\n\nint bot()\n{\n    return 2;\n}" >noeol.c &&
+	git add noeol.c &&
+	test_tick &&
+	git commit -m "add noeol.c (no trailing newline)" &&
+	sed "s/return 2/return 22/" noeol.c >tmp && mv tmp noeol.c &&
+	git commit -a -m "modify bot()" &&
+	printf "int top()\n{\n    return 1;\n}\n\nint bot()\n{\n    return 33;\n}\n" >noeol.c &&
+	git commit -a -m "modify bot() and add trailing newline"
+'
+
+# When the tracked function is at the end of a file with no trailing
+# newline, the "\ No newline at end of file" marker should appear.
+test_expect_success '-L no-newline-at-eof appears in tracked range' '
+	git log -L:bot:noeol.c --format= -1 HEAD~1 >actual &&
+	grep "No newline at end of file" actual
+'
+
+# When tracking a function that ends before the no-newline content,
+# the marker should not appear in the output.
+test_expect_success '-L no-newline-at-eof suppressed outside range' '
+	git log -L:top:noeol.c --format= >actual &&
+	! grep "No newline at end of file" actual
+'
+
+# When a commit removes a no-newline last line and replaces it with
+# a newline-terminated line, the marker should still appear (on the
+# old side of the diff).
+test_expect_success '-L no-newline-at-eof marker with deleted line' '
+	git log -L:bot:noeol.c --format= -1 >actual &&
+	grep "No newline at end of file" actual
+'
+
+test_expect_success 'setup for range boundary deletion test' '
+	git checkout --orphan range-boundary &&
+	git reset --hard &&
+	cat >boundary.c <<-\EOF &&
+	void above()
+	{
+	    return;
+	}
+
+	void tracked()
+	{
+	    int x = 1;
+	    int y = 2;
+	}
+
+	void below()
+	{
+	    return;
+	}
+	EOF
+	git add boundary.c &&
+	test_tick &&
+	git commit -m "add boundary.c" &&
+	cat >boundary.c <<-\EOF &&
+	void above()
+	{
+	    return;
+	}
+
+	void tracked()
+	{
+	    int x = 1;
+	    int y = 2;
+	}
+
+	void below_renamed()
+	{
+	    return 0;
+	}
+	EOF
+	git commit -a -m "modify below() only"
+'
+
+# When only a function below the tracked range is modified, the
+# tracked function should not produce a diff.
+test_expect_success '-L suppresses deletions outside tracked range' '
+	git log -L:tracked:boundary.c --format= >actual &&
+	test $(grep -c "^diff --git" actual) = 1
+'
+
+test_expect_success '-L with -S filters to string-count changes' '
+	git checkout parent-oids &&
+	git log -L:func2:file.c -S "F2 + 2" --format= >actual &&
+	# -S searches the whole file, not just the tracked range;
+	# combined with the -L range walk, this selects commits that
+	# both touch func2 and change the count of "F2 + 2" in the file.
+	test $(grep -c "^diff --git" actual) = 1 &&
+	grep "F2 + 2" actual
+'
+
+test_expect_success '-L with -G filters to diff-text matches' '
+	git checkout parent-oids &&
+	git log -L:func2:file.c -G "F2 [+] 2" --format= >actual &&
+	# -G greps the whole-file diff text, not just the tracked range;
+	# combined with -L, this selects commits that both touch func2
+	# and have "F2 + 2" in their diff.
+	test $(grep -c "^diff --git" actual) = 1 &&
+	grep "F2 + 2" actual
+'
+
 test_done
diff --git a/t/t4211/sha1/expect.beginning-of-file b/t/t4211/sha1/expect.beginning-of-file
index 91b4054..52c90af 100644
--- a/t/t4211/sha1/expect.beginning-of-file
+++ b/t/t4211/sha1/expect.beginning-of-file
@@ -5,6 +5,7 @@
     change at very beginning
 
 diff --git a/a.c b/a.c
+index bdb2bb1..5e709a1 100644
 --- a/a.c
 +++ b/a.c
 @@ -1,3 +1,4 @@
@@ -20,6 +21,7 @@
     touch both functions
 
 diff --git a/a.c b/a.c
+index 3233403..e51de13 100644
 --- a/a.c
 +++ b/a.c
 @@ -1,3 +1,3 @@
@@ -35,6 +37,8 @@
     initial
 
 diff --git a/a.c b/a.c
+new file mode 100644
+index 0000000..444e415
 --- /dev/null
 +++ b/a.c
 @@ -0,0 +1,3 @@
diff --git a/t/t4211/sha1/expect.end-of-file b/t/t4211/sha1/expect.end-of-file
index bd25bb2..c400368 100644
--- a/t/t4211/sha1/expect.end-of-file
+++ b/t/t4211/sha1/expect.end-of-file
@@ -5,9 +5,10 @@
     change back to complete line
 
 diff --git a/a.c b/a.c
+index 0b9cae5..5de3ea4 100644
 --- a/a.c
 +++ b/a.c
-@@ -20,3 +20,5 @@
+@@ -20,3 +20,5 @@ long f(long x)
  	printf("%ld\n", f(15));
  	return 0;
 -}
@@ -23,9 +24,10 @@
     change to an incomplete line at end
 
 diff --git a/a.c b/a.c
+index 5e709a1..0b9cae5 100644
 --- a/a.c
 +++ b/a.c
-@@ -20,3 +20,3 @@
+@@ -20,3 +20,3 @@ int main ()
  	printf("%ld\n", f(15));
  	return 0;
 -}
@@ -39,9 +41,10 @@
     touch both functions
 
 diff --git a/a.c b/a.c
+index 3233403..e51de13 100644
 --- a/a.c
 +++ b/a.c
-@@ -19,3 +19,3 @@
+@@ -19,3 +19,3 @@ int f(int x)
 -	printf("%d\n", f(15));
 +	printf("%ld\n", f(15));
  	return 0;
@@ -54,6 +57,8 @@
     initial
 
 diff --git a/a.c b/a.c
+new file mode 100644
+index 0000000..444e415
 --- /dev/null
 +++ b/a.c
 @@ -0,0 +18,3 @@
diff --git a/t/t4211/sha1/expect.move-support-f b/t/t4211/sha1/expect.move-support-f
index c905e01..ead6500 100644
--- a/t/t4211/sha1/expect.move-support-f
+++ b/t/t4211/sha1/expect.move-support-f
@@ -5,6 +5,7 @@
     another simple change
 
 diff --git a/b.c b/b.c
+index 5de3ea4..bf79c2f 100644
 --- a/b.c
 +++ b/b.c
 @@ -4,9 +4,9 @@
@@ -26,6 +27,7 @@
     touch both functions
 
 diff --git a/a.c b/a.c
+index 3233403..e51de13 100644
 --- a/a.c
 +++ b/a.c
 @@ -3,9 +3,9 @@
@@ -47,6 +49,7 @@
     change f()
 
 diff --git a/a.c b/a.c
+index 444e415..3233403 100644
 --- a/a.c
 +++ b/a.c
 @@ -3,8 +3,9 @@
@@ -67,6 +70,8 @@
     initial
 
 diff --git a/a.c b/a.c
+new file mode 100644
+index 0000000..444e415
 --- /dev/null
 +++ b/a.c
 @@ -0,0 +3,8 @@
diff --git a/t/t4211/sha1/expect.multiple b/t/t4211/sha1/expect.multiple
index 1eee8a7..a41851a 100644
--- a/t/t4211/sha1/expect.multiple
+++ b/t/t4211/sha1/expect.multiple
@@ -5,9 +5,10 @@
     change back to complete line
 
 diff --git a/a.c b/a.c
+index 0b9cae5..5de3ea4 100644
 --- a/a.c
 +++ b/a.c
-@@ -18,5 +18,7 @@
+@@ -18,5 +18,7 @@ long f(long x)
  int main ()
  {
  	printf("%ld\n", f(15));
@@ -25,9 +26,10 @@
     change to an incomplete line at end
 
 diff --git a/a.c b/a.c
+index 5e709a1..0b9cae5 100644
 --- a/a.c
 +++ b/a.c
-@@ -18,5 +18,5 @@
+@@ -18,5 +18,5 @@ long f(long x)
  int main ()
  {
  	printf("%ld\n", f(15));
@@ -43,6 +45,7 @@
     touch both functions
 
 diff --git a/a.c b/a.c
+index 3233403..e51de13 100644
 --- a/a.c
 +++ b/a.c
 @@ -3,9 +3,9 @@
@@ -71,6 +74,7 @@
     change f()
 
 diff --git a/a.c b/a.c
+index 444e415..3233403 100644
 --- a/a.c
 +++ b/a.c
 @@ -3,8 +3,9 @@
@@ -91,6 +95,8 @@
     initial
 
 diff --git a/a.c b/a.c
+new file mode 100644
+index 0000000..444e415
 --- /dev/null
 +++ b/a.c
 @@ -0,0 +3,8 @@
diff --git a/t/t4211/sha1/expect.multiple-overlapping b/t/t4211/sha1/expect.multiple-overlapping
index d930b6e..0ec9990 100644
--- a/t/t4211/sha1/expect.multiple-overlapping
+++ b/t/t4211/sha1/expect.multiple-overlapping
@@ -5,6 +5,7 @@
     change back to complete line
 
 diff --git a/a.c b/a.c
+index 0b9cae5..5de3ea4 100644
 --- a/a.c
 +++ b/a.c
 @@ -4,19 +4,21 @@
@@ -39,6 +40,7 @@
     change to an incomplete line at end
 
 diff --git a/a.c b/a.c
+index 5e709a1..0b9cae5 100644
 --- a/a.c
 +++ b/a.c
 @@ -4,19 +4,19 @@
@@ -71,6 +73,7 @@
     touch comment
 
 diff --git a/a.c b/a.c
+index e51de13..bdb2bb1 100644
 --- a/a.c
 +++ b/a.c
 @@ -3,19 +3,19 @@
@@ -102,6 +105,7 @@
     touch both functions
 
 diff --git a/a.c b/a.c
+index 3233403..e51de13 100644
 --- a/a.c
 +++ b/a.c
 @@ -3,19 +3,19 @@
@@ -134,6 +138,7 @@
     change f()
 
 diff --git a/a.c b/a.c
+index 444e415..3233403 100644
 --- a/a.c
 +++ b/a.c
 @@ -3,18 +3,19 @@
@@ -164,6 +169,8 @@
     initial
 
 diff --git a/a.c b/a.c
+new file mode 100644
+index 0000000..444e415
 --- /dev/null
 +++ b/a.c
 @@ -0,0 +3,18 @@
diff --git a/t/t4211/sha1/expect.multiple-superset b/t/t4211/sha1/expect.multiple-superset
index d930b6e..0ec9990 100644
--- a/t/t4211/sha1/expect.multiple-superset
+++ b/t/t4211/sha1/expect.multiple-superset
@@ -5,6 +5,7 @@
     change back to complete line
 
 diff --git a/a.c b/a.c
+index 0b9cae5..5de3ea4 100644
 --- a/a.c
 +++ b/a.c
 @@ -4,19 +4,21 @@
@@ -39,6 +40,7 @@
     change to an incomplete line at end
 
 diff --git a/a.c b/a.c
+index 5e709a1..0b9cae5 100644
 --- a/a.c
 +++ b/a.c
 @@ -4,19 +4,19 @@
@@ -71,6 +73,7 @@
     touch comment
 
 diff --git a/a.c b/a.c
+index e51de13..bdb2bb1 100644
 --- a/a.c
 +++ b/a.c
 @@ -3,19 +3,19 @@
@@ -102,6 +105,7 @@
     touch both functions
 
 diff --git a/a.c b/a.c
+index 3233403..e51de13 100644
 --- a/a.c
 +++ b/a.c
 @@ -3,19 +3,19 @@
@@ -134,6 +138,7 @@
     change f()
 
 diff --git a/a.c b/a.c
+index 444e415..3233403 100644
 --- a/a.c
 +++ b/a.c
 @@ -3,18 +3,19 @@
@@ -164,6 +169,8 @@
     initial
 
 diff --git a/a.c b/a.c
+new file mode 100644
+index 0000000..444e415
 --- /dev/null
 +++ b/a.c
 @@ -0,0 +3,18 @@
diff --git a/t/t4211/sha1/expect.no-assertion-error b/t/t4211/sha1/expect.no-assertion-error
index 994c37d..54c568f 100644
--- a/t/t4211/sha1/expect.no-assertion-error
+++ b/t/t4211/sha1/expect.no-assertion-error
@@ -5,6 +5,7 @@
     move within the file
 
 diff --git a/b.c b/b.c
+index bf79c2f..27c829c 100644
 --- a/b.c
 +++ b/b.c
 @@ -25,0 +18,9 @@
@@ -25,9 +26,10 @@
     change back to complete line
 
 diff --git a/a.c b/a.c
+index 0b9cae5..5de3ea4 100644
 --- a/a.c
 +++ b/a.c
-@@ -18,5 +18,7 @@
+@@ -18,5 +18,7 @@ long f(long x)
  int main ()
  {
  	printf("%ld\n", f(15));
@@ -45,9 +47,10 @@
     change to an incomplete line at end
 
 diff --git a/a.c b/a.c
+index 5e709a1..0b9cae5 100644
 --- a/a.c
 +++ b/a.c
-@@ -18,5 +18,5 @@
+@@ -18,5 +18,5 @@ long f(long x)
  int main ()
  {
  	printf("%ld\n", f(15));
@@ -63,9 +66,10 @@
     touch both functions
 
 diff --git a/a.c b/a.c
+index 3233403..e51de13 100644
 --- a/a.c
 +++ b/a.c
-@@ -17,5 +17,5 @@
+@@ -17,5 +17,5 @@ int f(int x)
  int main ()
  {
 -	printf("%d\n", f(15));
@@ -80,6 +84,8 @@
     initial
 
 diff --git a/a.c b/a.c
+new file mode 100644
+index 0000000..444e415
 --- /dev/null
 +++ b/a.c
 @@ -0,0 +16,5 @@
diff --git a/t/t4211/sha1/expect.parallel-change-f-to-main b/t/t4211/sha1/expect.parallel-change-f-to-main
index 052def8..65a8cc6 100644
--- a/t/t4211/sha1/expect.parallel-change-f-to-main
+++ b/t/t4211/sha1/expect.parallel-change-f-to-main
@@ -13,6 +13,7 @@
     another simple change
 
 diff --git a/b.c b/b.c
+index 5de3ea4..bf79c2f 100644
 --- a/b.c
 +++ b/b.c
 @@ -4,14 +4,14 @@
@@ -39,6 +40,7 @@
     change on another line of history while rename happens
 
 diff --git a/a.c b/a.c
+index 5de3ea4..01b5b65 100644
 --- a/a.c
 +++ b/a.c
 @@ -4,14 +4,14 @@
@@ -65,6 +67,7 @@
     touch comment
 
 diff --git a/a.c b/a.c
+index e51de13..bdb2bb1 100644
 --- a/a.c
 +++ b/a.c
 @@ -3,14 +3,14 @@
@@ -91,6 +94,7 @@
     touch both functions
 
 diff --git a/a.c b/a.c
+index 3233403..e51de13 100644
 --- a/a.c
 +++ b/a.c
 @@ -3,14 +3,14 @@
@@ -117,6 +121,7 @@
     change f()
 
 diff --git a/a.c b/a.c
+index 444e415..3233403 100644
 --- a/a.c
 +++ b/a.c
 @@ -3,13 +3,14 @@
@@ -142,6 +147,8 @@
     initial
 
 diff --git a/a.c b/a.c
+new file mode 100644
+index 0000000..444e415
 --- /dev/null
 +++ b/a.c
 @@ -0,0 +3,13 @@
diff --git a/t/t4211/sha1/expect.simple-f b/t/t4211/sha1/expect.simple-f
index a1f5bc4..b24ae40 100644
--- a/t/t4211/sha1/expect.simple-f
+++ b/t/t4211/sha1/expect.simple-f
@@ -5,6 +5,7 @@
     touch both functions
 
 diff --git a/a.c b/a.c
+index 3233403..e51de13 100644
 --- a/a.c
 +++ b/a.c
 @@ -3,9 +3,9 @@
@@ -26,6 +27,7 @@
     change f()
 
 diff --git a/a.c b/a.c
+index 444e415..3233403 100644
 --- a/a.c
 +++ b/a.c
 @@ -3,8 +3,9 @@
@@ -46,6 +48,8 @@
     initial
 
 diff --git a/a.c b/a.c
+new file mode 100644
+index 0000000..444e415
 --- /dev/null
 +++ b/a.c
 @@ -0,0 +3,8 @@
diff --git a/t/t4211/sha1/expect.simple-f-to-main b/t/t4211/sha1/expect.simple-f-to-main
index a475768..cd92100 100644
--- a/t/t4211/sha1/expect.simple-f-to-main
+++ b/t/t4211/sha1/expect.simple-f-to-main
@@ -5,6 +5,7 @@
     touch comment
 
 diff --git a/a.c b/a.c
+index e51de13..bdb2bb1 100644
 --- a/a.c
 +++ b/a.c
 @@ -3,14 +3,14 @@
@@ -31,6 +32,7 @@
     touch both functions
 
 diff --git a/a.c b/a.c
+index 3233403..e51de13 100644
 --- a/a.c
 +++ b/a.c
 @@ -3,14 +3,14 @@
@@ -57,6 +59,7 @@
     change f()
 
 diff --git a/a.c b/a.c
+index 444e415..3233403 100644
 --- a/a.c
 +++ b/a.c
 @@ -3,13 +3,14 @@
@@ -82,6 +85,8 @@
     initial
 
 diff --git a/a.c b/a.c
+new file mode 100644
+index 0000000..444e415
 --- /dev/null
 +++ b/a.c
 @@ -0,0 +3,13 @@
diff --git a/t/t4211/sha1/expect.simple-main b/t/t4211/sha1/expect.simple-main
index 39ce39b..ff31291 100644
--- a/t/t4211/sha1/expect.simple-main
+++ b/t/t4211/sha1/expect.simple-main
@@ -5,9 +5,10 @@
     change back to complete line
 
 diff --git a/a.c b/a.c
+index 0b9cae5..5de3ea4 100644
 --- a/a.c
 +++ b/a.c
-@@ -18,5 +18,5 @@
+@@ -18,5 +18,5 @@ long f(long x)
  int main ()
  {
  	printf("%ld\n", f(15));
@@ -23,9 +24,10 @@
     change to an incomplete line at end
 
 diff --git a/a.c b/a.c
+index 5e709a1..0b9cae5 100644
 --- a/a.c
 +++ b/a.c
-@@ -18,5 +18,5 @@
+@@ -18,5 +18,5 @@ long f(long x)
  int main ()
  {
  	printf("%ld\n", f(15));
@@ -41,9 +43,10 @@
     touch both functions
 
 diff --git a/a.c b/a.c
+index 3233403..e51de13 100644
 --- a/a.c
 +++ b/a.c
-@@ -17,5 +17,5 @@
+@@ -17,5 +17,5 @@ int f(int x)
  int main ()
  {
 -	printf("%d\n", f(15));
@@ -58,6 +61,8 @@
     initial
 
 diff --git a/a.c b/a.c
+new file mode 100644
+index 0000000..444e415
 --- /dev/null
 +++ b/a.c
 @@ -0,0 +16,5 @@
diff --git a/t/t4211/sha1/expect.simple-main-to-end b/t/t4211/sha1/expect.simple-main-to-end
index 8480bd9..4bef21e 100644
--- a/t/t4211/sha1/expect.simple-main-to-end
+++ b/t/t4211/sha1/expect.simple-main-to-end
@@ -5,9 +5,10 @@
     change back to complete line
 
 diff --git a/a.c b/a.c
+index 0b9cae5..5de3ea4 100644
 --- a/a.c
 +++ b/a.c
-@@ -18,5 +18,7 @@
+@@ -18,5 +18,7 @@ long f(long x)
  int main ()
  {
  	printf("%ld\n", f(15));
@@ -25,9 +26,10 @@
     change to an incomplete line at end
 
 diff --git a/a.c b/a.c
+index 5e709a1..0b9cae5 100644
 --- a/a.c
 +++ b/a.c
-@@ -18,5 +18,5 @@
+@@ -18,5 +18,5 @@ long f(long x)
  int main ()
  {
  	printf("%ld\n", f(15));
@@ -43,9 +45,10 @@
     touch both functions
 
 diff --git a/a.c b/a.c
+index 3233403..e51de13 100644
 --- a/a.c
 +++ b/a.c
-@@ -17,5 +17,5 @@
+@@ -17,5 +17,5 @@ int f(int x)
  int main ()
  {
 -	printf("%d\n", f(15));
@@ -60,6 +63,8 @@
     initial
 
 diff --git a/a.c b/a.c
+new file mode 100644
+index 0000000..444e415
 --- /dev/null
 +++ b/a.c
 @@ -0,0 +16,5 @@
diff --git a/t/t4211/sha1/expect.two-ranges b/t/t4211/sha1/expect.two-ranges
index c5164f3..aed0152 100644
--- a/t/t4211/sha1/expect.two-ranges
+++ b/t/t4211/sha1/expect.two-ranges
@@ -5,9 +5,10 @@
     change back to complete line
 
 diff --git a/a.c b/a.c
+index 0b9cae5..5de3ea4 100644
 --- a/a.c
 +++ b/a.c
-@@ -18,5 +18,5 @@
+@@ -18,5 +18,5 @@ long f(long x)
  int main ()
  {
  	printf("%ld\n", f(15));
@@ -23,9 +24,10 @@
     change to an incomplete line at end
 
 diff --git a/a.c b/a.c
+index 5e709a1..0b9cae5 100644
 --- a/a.c
 +++ b/a.c
-@@ -18,5 +18,5 @@
+@@ -18,5 +18,5 @@ long f(long x)
  int main ()
  {
  	printf("%ld\n", f(15));
@@ -41,6 +43,7 @@
     touch both functions
 
 diff --git a/a.c b/a.c
+index 3233403..e51de13 100644
 --- a/a.c
 +++ b/a.c
 @@ -3,9 +3,9 @@
@@ -69,6 +72,7 @@
     change f()
 
 diff --git a/a.c b/a.c
+index 444e415..3233403 100644
 --- a/a.c
 +++ b/a.c
 @@ -3,8 +3,9 @@
@@ -89,6 +93,8 @@
     initial
 
 diff --git a/a.c b/a.c
+new file mode 100644
+index 0000000..444e415
 --- /dev/null
 +++ b/a.c
 @@ -0,0 +3,8 @@
diff --git a/t/t4211/sha1/expect.vanishes-early b/t/t4211/sha1/expect.vanishes-early
index 1f7cd06..a413ad3 100644
--- a/t/t4211/sha1/expect.vanishes-early
+++ b/t/t4211/sha1/expect.vanishes-early
@@ -5,11 +5,10 @@
     change back to complete line
 
 diff --git a/a.c b/a.c
+index 0b9cae5..5de3ea4 100644
 --- a/a.c
 +++ b/a.c
-@@ -22,1 +24,1 @@
--}
-\ No newline at end of file
+@@ -23,0 +24,1 @@ int main ()
 +/* incomplete lines are bad! */
 
 commit 100b61a6f2f720f812620a9d10afb3a960ccb73c
@@ -19,9 +18,10 @@
     change to an incomplete line at end
 
 diff --git a/a.c b/a.c
+index 5e709a1..0b9cae5 100644
 --- a/a.c
 +++ b/a.c
-@@ -22,1 +22,1 @@
+@@ -22,1 +22,1 @@ int main ()
 -}
 +}
 \ No newline at end of file
@@ -33,6 +33,8 @@
     initial
 
 diff --git a/a.c b/a.c
+new file mode 100644
+index 0000000..444e415
 --- /dev/null
 +++ b/a.c
 @@ -0,0 +20,1 @@
diff --git a/t/t4211/sha256/expect.beginning-of-file b/t/t4211/sha256/expect.beginning-of-file
index 5adfdfc..e8d6232 100644
--- a/t/t4211/sha256/expect.beginning-of-file
+++ b/t/t4211/sha256/expect.beginning-of-file
@@ -5,6 +5,7 @@
     change at very beginning
 
 diff --git a/a.c b/a.c
+index 3a78aaf..d325124 100644
 --- a/a.c
 +++ b/a.c
 @@ -1,3 +1,4 @@
@@ -20,6 +21,7 @@
     touch both functions
 
 diff --git a/a.c b/a.c
+index 7a296b9..75c0119 100644
 --- a/a.c
 +++ b/a.c
 @@ -1,3 +1,3 @@
@@ -35,6 +37,8 @@
     initial
 
 diff --git a/a.c b/a.c
+new file mode 100644
+index 0000000..9f550c3
 --- /dev/null
 +++ b/a.c
 @@ -0,0 +1,3 @@
diff --git a/t/t4211/sha256/expect.end-of-file b/t/t4211/sha256/expect.end-of-file
index 03ab5c1..3b2e238 100644
--- a/t/t4211/sha256/expect.end-of-file
+++ b/t/t4211/sha256/expect.end-of-file
@@ -5,9 +5,10 @@
     change back to complete line
 
 diff --git a/a.c b/a.c
+index e4fa1d8..62c1fc2 100644
 --- a/a.c
 +++ b/a.c
-@@ -20,3 +20,5 @@
+@@ -20,3 +20,5 @@ long f(long x)
  	printf("%ld\n", f(15));
  	return 0;
 -}
@@ -23,9 +24,10 @@
     change to an incomplete line at end
 
 diff --git a/a.c b/a.c
+index d325124..e4fa1d8 100644
 --- a/a.c
 +++ b/a.c
-@@ -20,3 +20,3 @@
+@@ -20,3 +20,3 @@ int main ()
  	printf("%ld\n", f(15));
  	return 0;
 -}
@@ -39,9 +41,10 @@
     touch both functions
 
 diff --git a/a.c b/a.c
+index 7a296b9..75c0119 100644
 --- a/a.c
 +++ b/a.c
-@@ -19,3 +19,3 @@
+@@ -19,3 +19,3 @@ int f(int x)
 -	printf("%d\n", f(15));
 +	printf("%ld\n", f(15));
  	return 0;
@@ -54,6 +57,8 @@
     initial
 
 diff --git a/a.c b/a.c
+new file mode 100644
+index 0000000..9f550c3
 --- /dev/null
 +++ b/a.c
 @@ -0,0 +18,3 @@
diff --git a/t/t4211/sha256/expect.move-support-f b/t/t4211/sha256/expect.move-support-f
index 223b4ed..f49abce 100644
--- a/t/t4211/sha256/expect.move-support-f
+++ b/t/t4211/sha256/expect.move-support-f
@@ -5,6 +5,7 @@
     another simple change
 
 diff --git a/b.c b/b.c
+index 62c1fc2..69cb69c 100644
 --- a/b.c
 +++ b/b.c
 @@ -4,9 +4,9 @@
@@ -26,6 +27,7 @@
     touch both functions
 
 diff --git a/a.c b/a.c
+index 7a296b9..75c0119 100644
 --- a/a.c
 +++ b/a.c
 @@ -3,9 +3,9 @@
@@ -47,6 +49,7 @@
     change f()
 
 diff --git a/a.c b/a.c
+index 9f550c3..7a296b9 100644
 --- a/a.c
 +++ b/a.c
 @@ -3,8 +3,9 @@
@@ -67,6 +70,8 @@
     initial
 
 diff --git a/a.c b/a.c
+new file mode 100644
+index 0000000..9f550c3
 --- /dev/null
 +++ b/a.c
 @@ -0,0 +3,8 @@
diff --git a/t/t4211/sha256/expect.multiple b/t/t4211/sha256/expect.multiple
index dbd987b..0dee50f 100644
--- a/t/t4211/sha256/expect.multiple
+++ b/t/t4211/sha256/expect.multiple
@@ -5,9 +5,10 @@
     change back to complete line
 
 diff --git a/a.c b/a.c
+index e4fa1d8..62c1fc2 100644
 --- a/a.c
 +++ b/a.c
-@@ -18,5 +18,7 @@
+@@ -18,5 +18,7 @@ long f(long x)
  int main ()
  {
  	printf("%ld\n", f(15));
@@ -25,9 +26,10 @@
     change to an incomplete line at end
 
 diff --git a/a.c b/a.c
+index d325124..e4fa1d8 100644
 --- a/a.c
 +++ b/a.c
-@@ -18,5 +18,5 @@
+@@ -18,5 +18,5 @@ long f(long x)
  int main ()
  {
  	printf("%ld\n", f(15));
@@ -43,6 +45,7 @@
     touch both functions
 
 diff --git a/a.c b/a.c
+index 7a296b9..75c0119 100644
 --- a/a.c
 +++ b/a.c
 @@ -3,9 +3,9 @@
@@ -71,6 +74,7 @@
     change f()
 
 diff --git a/a.c b/a.c
+index 9f550c3..7a296b9 100644
 --- a/a.c
 +++ b/a.c
 @@ -3,8 +3,9 @@
@@ -91,6 +95,8 @@
     initial
 
 diff --git a/a.c b/a.c
+new file mode 100644
+index 0000000..9f550c3
 --- /dev/null
 +++ b/a.c
 @@ -0,0 +3,8 @@
diff --git a/t/t4211/sha256/expect.multiple-overlapping b/t/t4211/sha256/expect.multiple-overlapping
index 9015a45..b8c260e 100644
--- a/t/t4211/sha256/expect.multiple-overlapping
+++ b/t/t4211/sha256/expect.multiple-overlapping
@@ -5,6 +5,7 @@
     change back to complete line
 
 diff --git a/a.c b/a.c
+index e4fa1d8..62c1fc2 100644
 --- a/a.c
 +++ b/a.c
 @@ -4,19 +4,21 @@
@@ -39,6 +40,7 @@
     change to an incomplete line at end
 
 diff --git a/a.c b/a.c
+index d325124..e4fa1d8 100644
 --- a/a.c
 +++ b/a.c
 @@ -4,19 +4,19 @@
@@ -71,6 +73,7 @@
     touch comment
 
 diff --git a/a.c b/a.c
+index 75c0119..3a78aaf 100644
 --- a/a.c
 +++ b/a.c
 @@ -3,19 +3,19 @@
@@ -102,6 +105,7 @@
     touch both functions
 
 diff --git a/a.c b/a.c
+index 7a296b9..75c0119 100644
 --- a/a.c
 +++ b/a.c
 @@ -3,19 +3,19 @@
@@ -134,6 +138,7 @@
     change f()
 
 diff --git a/a.c b/a.c
+index 9f550c3..7a296b9 100644
 --- a/a.c
 +++ b/a.c
 @@ -3,18 +3,19 @@
@@ -164,6 +169,8 @@
     initial
 
 diff --git a/a.c b/a.c
+new file mode 100644
+index 0000000..9f550c3
 --- /dev/null
 +++ b/a.c
 @@ -0,0 +3,18 @@
diff --git a/t/t4211/sha256/expect.multiple-superset b/t/t4211/sha256/expect.multiple-superset
index 9015a45..b8c260e 100644
--- a/t/t4211/sha256/expect.multiple-superset
+++ b/t/t4211/sha256/expect.multiple-superset
@@ -5,6 +5,7 @@
     change back to complete line
 
 diff --git a/a.c b/a.c
+index e4fa1d8..62c1fc2 100644
 --- a/a.c
 +++ b/a.c
 @@ -4,19 +4,21 @@
@@ -39,6 +40,7 @@
     change to an incomplete line at end
 
 diff --git a/a.c b/a.c
+index d325124..e4fa1d8 100644
 --- a/a.c
 +++ b/a.c
 @@ -4,19 +4,19 @@
@@ -71,6 +73,7 @@
     touch comment
 
 diff --git a/a.c b/a.c
+index 75c0119..3a78aaf 100644
 --- a/a.c
 +++ b/a.c
 @@ -3,19 +3,19 @@
@@ -102,6 +105,7 @@
     touch both functions
 
 diff --git a/a.c b/a.c
+index 7a296b9..75c0119 100644
 --- a/a.c
 +++ b/a.c
 @@ -3,19 +3,19 @@
@@ -134,6 +138,7 @@
     change f()
 
 diff --git a/a.c b/a.c
+index 9f550c3..7a296b9 100644
 --- a/a.c
 +++ b/a.c
 @@ -3,18 +3,19 @@
@@ -164,6 +169,8 @@
     initial
 
 diff --git a/a.c b/a.c
+new file mode 100644
+index 0000000..9f550c3
 --- /dev/null
 +++ b/a.c
 @@ -0,0 +3,18 @@
diff --git a/t/t4211/sha256/expect.no-assertion-error b/t/t4211/sha256/expect.no-assertion-error
index 36ed12a..c25f2ce 100644
--- a/t/t4211/sha256/expect.no-assertion-error
+++ b/t/t4211/sha256/expect.no-assertion-error
@@ -5,6 +5,7 @@
     move within the file
 
 diff --git a/b.c b/b.c
+index 69cb69c..a0d566e 100644
 --- a/b.c
 +++ b/b.c
 @@ -25,0 +18,9 @@
@@ -25,9 +26,10 @@
     change back to complete line
 
 diff --git a/a.c b/a.c
+index e4fa1d8..62c1fc2 100644
 --- a/a.c
 +++ b/a.c
-@@ -18,5 +18,7 @@
+@@ -18,5 +18,7 @@ long f(long x)
  int main ()
  {
  	printf("%ld\n", f(15));
@@ -45,9 +47,10 @@
     change to an incomplete line at end
 
 diff --git a/a.c b/a.c
+index d325124..e4fa1d8 100644
 --- a/a.c
 +++ b/a.c
-@@ -18,5 +18,5 @@
+@@ -18,5 +18,5 @@ long f(long x)
  int main ()
  {
  	printf("%ld\n", f(15));
@@ -63,9 +66,10 @@
     touch both functions
 
 diff --git a/a.c b/a.c
+index 7a296b9..75c0119 100644
 --- a/a.c
 +++ b/a.c
-@@ -17,5 +17,5 @@
+@@ -17,5 +17,5 @@ int f(int x)
  int main ()
  {
 -	printf("%d\n", f(15));
@@ -80,6 +84,8 @@
     initial
 
 diff --git a/a.c b/a.c
+new file mode 100644
+index 0000000..9f550c3
 --- /dev/null
 +++ b/a.c
 @@ -0,0 +16,5 @@
diff --git a/t/t4211/sha256/expect.parallel-change-f-to-main b/t/t4211/sha256/expect.parallel-change-f-to-main
index e68f892..3178989 100644
--- a/t/t4211/sha256/expect.parallel-change-f-to-main
+++ b/t/t4211/sha256/expect.parallel-change-f-to-main
@@ -13,6 +13,7 @@
     another simple change
 
 diff --git a/b.c b/b.c
+index 62c1fc2..69cb69c 100644
 --- a/b.c
 +++ b/b.c
 @@ -4,14 +4,14 @@
@@ -39,6 +40,7 @@
     change on another line of history while rename happens
 
 diff --git a/a.c b/a.c
+index 62c1fc2..e1e8475 100644
 --- a/a.c
 +++ b/a.c
 @@ -4,14 +4,14 @@
@@ -65,6 +67,7 @@
     touch comment
 
 diff --git a/a.c b/a.c
+index 75c0119..3a78aaf 100644
 --- a/a.c
 +++ b/a.c
 @@ -3,14 +3,14 @@
@@ -91,6 +94,7 @@
     touch both functions
 
 diff --git a/a.c b/a.c
+index 7a296b9..75c0119 100644
 --- a/a.c
 +++ b/a.c
 @@ -3,14 +3,14 @@
@@ -117,6 +121,7 @@
     change f()
 
 diff --git a/a.c b/a.c
+index 9f550c3..7a296b9 100644
 --- a/a.c
 +++ b/a.c
 @@ -3,13 +3,14 @@
@@ -142,6 +147,8 @@
     initial
 
 diff --git a/a.c b/a.c
+new file mode 100644
+index 0000000..9f550c3
 --- /dev/null
 +++ b/a.c
 @@ -0,0 +3,13 @@
diff --git a/t/t4211/sha256/expect.simple-f b/t/t4211/sha256/expect.simple-f
index 65508d7..983c711 100644
--- a/t/t4211/sha256/expect.simple-f
+++ b/t/t4211/sha256/expect.simple-f
@@ -5,6 +5,7 @@
     touch both functions
 
 diff --git a/a.c b/a.c
+index 7a296b9..75c0119 100644
 --- a/a.c
 +++ b/a.c
 @@ -3,9 +3,9 @@
@@ -26,6 +27,7 @@
     change f()
 
 diff --git a/a.c b/a.c
+index 9f550c3..7a296b9 100644
 --- a/a.c
 +++ b/a.c
 @@ -3,8 +3,9 @@
@@ -46,6 +48,8 @@
     initial
 
 diff --git a/a.c b/a.c
+new file mode 100644
+index 0000000..9f550c3
 --- /dev/null
 +++ b/a.c
 @@ -0,0 +3,8 @@
diff --git a/t/t4211/sha256/expect.simple-f-to-main b/t/t4211/sha256/expect.simple-f-to-main
index 77b721c..e67fa01 100644
--- a/t/t4211/sha256/expect.simple-f-to-main
+++ b/t/t4211/sha256/expect.simple-f-to-main
@@ -5,6 +5,7 @@
     touch comment
 
 diff --git a/a.c b/a.c
+index 75c0119..3a78aaf 100644
 --- a/a.c
 +++ b/a.c
 @@ -3,14 +3,14 @@
@@ -31,6 +32,7 @@
     touch both functions
 
 diff --git a/a.c b/a.c
+index 7a296b9..75c0119 100644
 --- a/a.c
 +++ b/a.c
 @@ -3,14 +3,14 @@
@@ -57,6 +59,7 @@
     change f()
 
 diff --git a/a.c b/a.c
+index 9f550c3..7a296b9 100644
 --- a/a.c
 +++ b/a.c
 @@ -3,13 +3,14 @@
@@ -82,6 +85,8 @@
     initial
 
 diff --git a/a.c b/a.c
+new file mode 100644
+index 0000000..9f550c3
 --- /dev/null
 +++ b/a.c
 @@ -0,0 +3,13 @@
diff --git a/t/t4211/sha256/expect.simple-main b/t/t4211/sha256/expect.simple-main
index d20708c..0792b27 100644
--- a/t/t4211/sha256/expect.simple-main
+++ b/t/t4211/sha256/expect.simple-main
@@ -5,9 +5,10 @@
     change back to complete line
 
 diff --git a/a.c b/a.c
+index e4fa1d8..62c1fc2 100644
 --- a/a.c
 +++ b/a.c
-@@ -18,5 +18,5 @@
+@@ -18,5 +18,5 @@ long f(long x)
  int main ()
  {
  	printf("%ld\n", f(15));
@@ -23,9 +24,10 @@
     change to an incomplete line at end
 
 diff --git a/a.c b/a.c
+index d325124..e4fa1d8 100644
 --- a/a.c
 +++ b/a.c
-@@ -18,5 +18,5 @@
+@@ -18,5 +18,5 @@ long f(long x)
  int main ()
  {
  	printf("%ld\n", f(15));
@@ -41,9 +43,10 @@
     touch both functions
 
 diff --git a/a.c b/a.c
+index 7a296b9..75c0119 100644
 --- a/a.c
 +++ b/a.c
-@@ -17,5 +17,5 @@
+@@ -17,5 +17,5 @@ int f(int x)
  int main ()
  {
 -	printf("%d\n", f(15));
@@ -58,6 +61,8 @@
     initial
 
 diff --git a/a.c b/a.c
+new file mode 100644
+index 0000000..9f550c3
 --- /dev/null
 +++ b/a.c
 @@ -0,0 +16,5 @@
diff --git a/t/t4211/sha256/expect.simple-main-to-end b/t/t4211/sha256/expect.simple-main-to-end
index 617cdf3..d3bd7c7 100644
--- a/t/t4211/sha256/expect.simple-main-to-end
+++ b/t/t4211/sha256/expect.simple-main-to-end
@@ -5,9 +5,10 @@
     change back to complete line
 
 diff --git a/a.c b/a.c
+index e4fa1d8..62c1fc2 100644
 --- a/a.c
 +++ b/a.c
-@@ -18,5 +18,7 @@
+@@ -18,5 +18,7 @@ long f(long x)
  int main ()
  {
  	printf("%ld\n", f(15));
@@ -25,9 +26,10 @@
     change to an incomplete line at end
 
 diff --git a/a.c b/a.c
+index d325124..e4fa1d8 100644
 --- a/a.c
 +++ b/a.c
-@@ -18,5 +18,5 @@
+@@ -18,5 +18,5 @@ long f(long x)
  int main ()
  {
  	printf("%ld\n", f(15));
@@ -43,9 +45,10 @@
     touch both functions
 
 diff --git a/a.c b/a.c
+index 7a296b9..75c0119 100644
 --- a/a.c
 +++ b/a.c
-@@ -17,5 +17,5 @@
+@@ -17,5 +17,5 @@ int f(int x)
  int main ()
  {
 -	printf("%d\n", f(15));
@@ -60,6 +63,8 @@
     initial
 
 diff --git a/a.c b/a.c
+new file mode 100644
+index 0000000..9f550c3
 --- /dev/null
 +++ b/a.c
 @@ -0,0 +16,5 @@
diff --git a/t/t4211/sha256/expect.two-ranges b/t/t4211/sha256/expect.two-ranges
index 6a94d3b..7735b19 100644
--- a/t/t4211/sha256/expect.two-ranges
+++ b/t/t4211/sha256/expect.two-ranges
@@ -5,9 +5,10 @@
     change back to complete line
 
 diff --git a/a.c b/a.c
+index e4fa1d8..62c1fc2 100644
 --- a/a.c
 +++ b/a.c
-@@ -18,5 +18,5 @@
+@@ -18,5 +18,5 @@ long f(long x)
  int main ()
  {
  	printf("%ld\n", f(15));
@@ -23,9 +24,10 @@
     change to an incomplete line at end
 
 diff --git a/a.c b/a.c
+index d325124..e4fa1d8 100644
 --- a/a.c
 +++ b/a.c
-@@ -18,5 +18,5 @@
+@@ -18,5 +18,5 @@ long f(long x)
  int main ()
  {
  	printf("%ld\n", f(15));
@@ -41,6 +43,7 @@
     touch both functions
 
 diff --git a/a.c b/a.c
+index 7a296b9..75c0119 100644
 --- a/a.c
 +++ b/a.c
 @@ -3,9 +3,9 @@
@@ -69,6 +72,7 @@
     change f()
 
 diff --git a/a.c b/a.c
+index 9f550c3..7a296b9 100644
 --- a/a.c
 +++ b/a.c
 @@ -3,8 +3,9 @@
@@ -89,6 +93,8 @@
     initial
 
 diff --git a/a.c b/a.c
+new file mode 100644
+index 0000000..9f550c3
 --- /dev/null
 +++ b/a.c
 @@ -0,0 +3,8 @@
diff --git a/t/t4211/sha256/expect.vanishes-early b/t/t4211/sha256/expect.vanishes-early
index 11ec9bd..bc33b96 100644
--- a/t/t4211/sha256/expect.vanishes-early
+++ b/t/t4211/sha256/expect.vanishes-early
@@ -5,11 +5,10 @@
     change back to complete line
 
 diff --git a/a.c b/a.c
+index e4fa1d8..62c1fc2 100644
 --- a/a.c
 +++ b/a.c
-@@ -22,1 +24,1 @@
--}
-\ No newline at end of file
+@@ -23,0 +24,1 @@ int main ()
 +/* incomplete lines are bad! */
 
 commit 29f32ac3141c48b22803e5c4127b719917b67d0f8ca8c5248bebfa2a19f7da10
@@ -19,9 +18,10 @@
     change to an incomplete line at end
 
 diff --git a/a.c b/a.c
+index d325124..e4fa1d8 100644
 --- a/a.c
 +++ b/a.c
-@@ -22,1 +22,1 @@
+@@ -22,1 +22,1 @@ int main ()
 -}
 +}
 \ No newline at end of file
@@ -33,6 +33,8 @@
     initial
 
 diff --git a/a.c b/a.c
+new file mode 100644
+index 0000000..9f550c3
 --- /dev/null
 +++ b/a.c
 @@ -0,0 +20,1 @@
diff --git a/t/t4215-log-skewed-merges.sh b/t/t4215-log-skewed-merges.sh
index 28d0779..1612f05 100755
--- a/t/t4215-log-skewed-merges.sh
+++ b/t/t4215-log-skewed-merges.sh
@@ -370,4 +370,148 @@
 	EOF
 '
 
+test_expect_success 'log --graph --graph-lane-limit=2 limited to two lanes' '
+	check_graph --graph-lane-limit=2 M_7 <<-\EOF
+	*-.   7_M4
+	|\ \
+	| | * 7_G
+	| | * 7_F
+	| * ~ 7_E
+	| * ~ 7_D
+	* | ~ 7_C
+	| |/
+	|/|
+	* | 7_B
+	|/
+	* 7_A
+	EOF
+'
+
+test_expect_success 'log --graph --graph-lane-limit=1 truncate mid octopus merge' '
+	check_graph --graph-lane-limit=1 M_7 <<-\EOF
+	*-~  7_M4
+	|\~
+	| ~ 7_G
+	| ~ 7_F
+	| * 7_E
+	| * 7_D
+	* ~ 7_C
+	| ~
+	|/~
+	* ~ 7_B
+	|/
+	* 7_A
+	EOF
+'
+
+test_expect_success 'log --graph --graph-lane-limit=3 limited to three lanes' '
+	check_graph --graph-lane-limit=3 M_1 M_3 M_5 M_7 <<-\EOF
+	*   7_M1
+	|\
+	| | *   7_M2
+	| | |\
+	| | | * 7_H
+	| | | ~ 7_M3
+	| | | ~ 7_J
+	| | | ~ 7_I
+	| | | ~ 7_M4
+	| |_|_~
+	|/| | ~
+	| | |_~
+	| |/| ~
+	| | | ~
+	| | |/~
+	| | * ~ 7_G
+	| | | ~
+	| | |/~
+	| | * ~ 7_F
+	| * | ~ 7_E
+	| | |/~
+	| |/| ~
+	| * | ~ 7_D
+	| | |/
+	| |/|
+	* | | 7_C
+	| |/
+	|/|
+	* | 7_B
+	|/
+	* 7_A
+	EOF
+'
+
+test_expect_success 'log --graph --graph-lane-limit=6 check if it only shows first of 3 parent merge' '
+	check_graph --graph-lane-limit=6 M_1 M_3 M_5 M_7 <<-\EOF
+	*   7_M1
+	|\
+	| | *   7_M2
+	| | |\
+	| | | * 7_H
+	| | | | *   7_M3
+	| | | | |\
+	| | | | | * 7_J
+	| | | | * | 7_I
+	| | | | | | * 7_M4
+	| |_|_|_|_|/~
+	|/| | | | |/~
+	| | |_|_|/| ~
+	| |/| | | |/
+	| | | |_|/|
+	| | |/| | |
+	| | * | | | 7_G
+	| | | |_|/
+	| | |/| |
+	| | * | | 7_F
+	| * | | | 7_E
+	| | |/ /
+	| |/| |
+	| * | | 7_D
+	| | |/
+	| |/|
+	* | | 7_C
+	| |/
+	|/|
+	* | 7_B
+	|/
+	* 7_A
+	EOF
+'
+
+test_expect_success 'log --graph --graph-lane-limit=7 check if it shows all 3 parent merge' '
+	check_graph --graph-lane-limit=7 M_1 M_3 M_5 M_7 <<-\EOF
+	*   7_M1
+	|\
+	| | *   7_M2
+	| | |\
+	| | | * 7_H
+	| | | | *   7_M3
+	| | | | |\
+	| | | | | * 7_J
+	| | | | * | 7_I
+	| | | | | | *   7_M4
+	| |_|_|_|_|/|\
+	|/| | | | |/ /
+	| | |_|_|/| /
+	| |/| | | |/
+	| | | |_|/|
+	| | |/| | |
+	| | * | | | 7_G
+	| | | |_|/
+	| | |/| |
+	| | * | | 7_F
+	| * | | | 7_E
+	| | |/ /
+	| |/| |
+	| * | | 7_D
+	| | |/
+	| |/|
+	* | | 7_C
+	| |/
+	|/|
+	* | 7_B
+	|/
+	* 7_A
+	EOF
+'
+
 test_done
diff --git a/t/t5000-tar-tree.sh b/t/t5000-tar-tree.sh
index 5465054..a8c2853 100755
--- a/t/t5000-tar-tree.sh
+++ b/t/t5000-tar-tree.sh
@@ -503,8 +503,8 @@
 # would generate the whole 64GB).
 test_expect_success LONG_IS_64BIT 'generate tar with huge size' '
 	{
-		git archive HEAD
-		echo $? >exit-code
+		{ ret=0 && git archive HEAD || ret=$?; } &&
+		echo "$ret" >exit-code
 	} | test_copy_bytes 4096 >huge.tar &&
 	echo 141 >expect &&
 	test_cmp expect exit-code
diff --git a/t/t5310-pack-bitmaps.sh b/t/t5310-pack-bitmaps.sh
index f693cb5..efeb715 100755
--- a/t/t5310-pack-bitmaps.sh
+++ b/t/t5310-pack-bitmaps.sh
@@ -648,4 +648,28 @@
 	test_grep corrupted.bitmap.index stderr
 '
 
+test_expect_success 'test-tool bitmap write determines bitmap selection' '
+	test_when_finished "rm -fr bitmap-write-helper" &&
+	git init bitmap-write-helper &&
+	(
+		cd bitmap-write-helper &&
+
+		test_commit_bulk 64 &&
+		git repack -ad &&
+
+		pack="$(ls .git/objects/pack/pack-*.pack)" &&
+
+		git rev-parse HEAD >in &&
+		test-tool bitmap write "$(basename $pack)" <in &&
+
+		test-tool bitmap list-commits >bitmaps.raw &&
+		sort bitmaps.raw >bitmaps &&
+		test_cmp in bitmaps &&
+
+		git rev-list --count --objects --use-bitmap-index HEAD >actual &&
+		git rev-list --count --objects HEAD >expect &&
+		test_cmp expect actual
+	)
+'
+
 test_done
diff --git a/t/t5317-pack-objects-filter-objects.sh b/t/t5317-pack-objects-filter-objects.sh
index 501d715..dddb79b 100755
--- a/t/t5317-pack-objects-filter-objects.sh
+++ b/t/t5317-pack-objects-filter-objects.sh
@@ -478,4 +478,129 @@
 	EOF
 '
 
+# Test that --path-walk produces the same object set as standard traversal
+# when using sparse:oid filters with cone-mode patterns.
+#
+# The sparse:oid filter restricts only blobs, not trees. Both standard
+# and path-walk should produce identical sets of blobs, commits, and trees.
+
+test_expect_success 'setup pw_sparse for path-walk comparison' '
+	git init pw_sparse &&
+	mkdir -p pw_sparse/inc/sub pw_sparse/exc/sub &&
+
+	for n in 1 2
+	do
+		echo "inc $n" >pw_sparse/inc/file$n &&
+		echo "inc sub $n" >pw_sparse/inc/sub/file$n &&
+		echo "exc $n" >pw_sparse/exc/file$n &&
+		echo "exc sub $n" >pw_sparse/exc/sub/file$n &&
+		echo "root $n" >pw_sparse/root$n || return 1
+	done &&
+
+	git -C pw_sparse add . &&
+	git -C pw_sparse commit -m "first" &&
+
+	echo "inc 1 modified" >pw_sparse/inc/file1 &&
+	echo "exc 1 modified" >pw_sparse/exc/file1 &&
+	echo "root 1 modified" >pw_sparse/root1 &&
+	git -C pw_sparse add . &&
+	git -C pw_sparse commit -m "second" &&
+
+	# Cone-mode sparse pattern: include root + inc/
+	printf "/*\n!/*/\n/inc/\n" |
+	git -C pw_sparse hash-object -w --stdin >sparse_oid
+'
+
+test_expect_success 'sparse:oid with --path-walk produces same blobs' '
+	oid=$(cat sparse_oid) &&
+
+	git -C pw_sparse pack-objects --revs --stdout \
+		--filter=sparse:oid=$oid >standard.pack <<-EOF &&
+	HEAD
+	EOF
+	git -C pw_sparse index-pack ../standard.pack &&
+	git -C pw_sparse verify-pack -v ../standard.pack >standard_verify &&
+
+	git -C pw_sparse pack-objects --revs --stdout \
+		--path-walk --filter=sparse:oid=$oid >pathwalk.pack <<-EOF &&
+	HEAD
+	EOF
+	git -C pw_sparse index-pack ../pathwalk.pack &&
+	git -C pw_sparse verify-pack -v ../pathwalk.pack >pathwalk_verify &&
+
+	# Blobs must match exactly
+	grep -E "^[0-9a-f]{40} blob" standard_verify |
+	awk "{print \$1}" | sort >standard_blobs &&
+	grep -E "^[0-9a-f]{40} blob" pathwalk_verify |
+	awk "{print \$1}" | sort >pathwalk_blobs &&
+	test_cmp standard_blobs pathwalk_blobs &&
+
+	# Commits must match exactly
+	grep -E "^[0-9a-f]{40} commit" standard_verify |
+	awk "{print \$1}" | sort >standard_commits &&
+	grep -E "^[0-9a-f]{40} commit" pathwalk_verify |
+	awk "{print \$1}" | sort >pathwalk_commits &&
+	test_cmp standard_commits pathwalk_commits
+'
+
+test_expect_success 'sparse:oid with --path-walk includes all trees' '
+	# The sparse:oid filter restricts only blobs, not trees.
+	# Both standard and path-walk should include the same trees.
+	grep -E "^[0-9a-f]{40} tree" standard_verify |
+	awk "{print \$1}" | sort >standard_trees &&
+	grep -E "^[0-9a-f]{40} tree" pathwalk_verify |
+	awk "{print \$1}" | sort >pathwalk_trees &&
+
+	test_cmp standard_trees pathwalk_trees
+'
+
+# Test the edge case where the same tree/blob OID appears at both an
+# in-cone and out-of-cone path. When sibling directories have identical
+# contents, they share a tree OID. The path-walk defers marking objects
+# SEEN until after checking sparse patterns, so an object at an out-of-cone
+# path can still be discovered at an in-cone path.
+
+test_expect_success 'setup pw_shared for shared OID across cone boundary' '
+	git init pw_shared &&
+	mkdir pw_shared/aaa pw_shared/zzz &&
+	echo "shared content" >pw_shared/aaa/file &&
+	echo "shared content" >pw_shared/zzz/file &&
+	echo "root file" >pw_shared/rootfile &&
+	git -C pw_shared add . &&
+	git -C pw_shared commit -m "aaa and zzz share tree OID" &&
+
+	# Verify they share a tree OID
+	aaa_tree=$(git -C pw_shared rev-parse HEAD:aaa) &&
+	zzz_tree=$(git -C pw_shared rev-parse HEAD:zzz) &&
+	test "$aaa_tree" = "$zzz_tree" &&
+
+	# Cone pattern: include root + zzz/ (not aaa/)
+	printf "/*\n!/*/\n/zzz/\n" |
+	git -C pw_shared hash-object -w --stdin >shared_sparse_oid
+'
+
+test_expect_success 'shared tree OID: --path-walk blobs match standard' '
+	oid=$(cat shared_sparse_oid) &&
+
+	git -C pw_shared pack-objects --revs --stdout \
+		--filter=sparse:oid=$oid >shared_std.pack <<-EOF &&
+	HEAD
+	EOF
+	git -C pw_shared index-pack ../shared_std.pack &&
+	git -C pw_shared verify-pack -v ../shared_std.pack >shared_std_verify &&
+
+	git -C pw_shared pack-objects --revs --stdout \
+		--path-walk --filter=sparse:oid=$oid >shared_pw.pack <<-EOF &&
+	HEAD
+	EOF
+	git -C pw_shared index-pack ../shared_pw.pack &&
+	git -C pw_shared verify-pack -v ../shared_pw.pack >shared_pw_verify &&
+
+	grep -E "^[0-9a-f]{40} blob" shared_std_verify |
+	awk "{print \$1}" | sort >shared_std_blobs &&
+	grep -E "^[0-9a-f]{40} blob" shared_pw_verify |
+	awk "{print \$1}" | sort >shared_pw_blobs &&
+	test_cmp shared_std_blobs shared_pw_blobs
+'
+
 test_done
diff --git a/t/t5318-commit-graph.sh b/t/t5318-commit-graph.sh
index 98c6910..1c40f90 100755
--- a/t/t5318-commit-graph.sh
+++ b/t/t5318-commit-graph.sh
@@ -417,6 +417,26 @@
 	test_cmp full/.git/objects/info/commit-graph commit-graph-upgraded
 '
 
+test_expect_success TIME_IS_64BIT,TIME_T_IS_64BIT 'overflow chunk when replacing commit-graph' '
+	test_when_finished "rm -rf repo" &&
+	git init repo &&
+	(
+		cd repo &&
+		cat >commit <<-EOF &&
+		tree $(test_oid empty_tree)
+		author Example <committer@example.com> 9223372036854775 +0000
+		committer Example <committer@example.com> 9223372036854775 +0000
+
+		Weird commit date
+		EOF
+		commit_id=$(git hash-object -t commit -w commit) &&
+		git reset --hard "$commit_id" &&
+		git commit-graph write --reachable &&
+		git commit-graph write --reachable --split=replace &&
+		git log
+	)
+'
+
 # the verify tests below expect the commit-graph to contain
 # exactly the commits reachable from the commits/8 branch.
 # If the file changes the set of commits in the list, then the
diff --git a/t/t5319-multi-pack-index.sh b/t/t5319-multi-pack-index.sh
index 58e0b68..fa0d404 100755
--- a/t/t5319-multi-pack-index.sh
+++ b/t/t5319-multi-pack-index.sh
@@ -21,7 +21,7 @@
 	EXTRA_CHUNKS="$5"
 	{
 		cat <<-EOF &&
-		header: 4d494458 2 $HASH_LEN $NUM_CHUNKS $NUM_PACKS
+		header: 4d494458 1 $HASH_LEN $NUM_CHUNKS $NUM_PACKS
 		chunks: pack-names oid-fanout oid-lookup object-offsets$EXTRA_CHUNKS
 		num_objects: $NUM_OBJECTS
 		packs:
diff --git a/t/t5331-pack-objects-stdin.sh b/t/t5331-pack-objects-stdin.sh
index 7eb79bc..c74b586 100755
--- a/t/t5331-pack-objects-stdin.sh
+++ b/t/t5331-pack-objects-stdin.sh
@@ -415,4 +415,109 @@
 	stdin_packs__follow_with_only HEAD HEAD^{tree}
 '
 
+test_expect_success '--stdin-packs=follow with open-excluded packs' '
+	test_when_finished "rm -fr repo" &&
+
+	git init repo &&
+	(
+		cd repo &&
+		git config set maintenance.auto false &&
+
+		git branch -M main &&
+
+		# Create the following commit structure:
+		#
+		#   A <-- B <-- D     (main)
+		#         ^
+		#          \
+		#           C        (other)
+		test_commit A &&
+		test_commit B &&
+		git checkout -B other &&
+		test_commit C &&
+		git checkout main &&
+		test_commit D &&
+
+		A="$(echo A | git pack-objects --revs $packdir/pack)" &&
+		B="$(echo A..B | git pack-objects --revs $packdir/pack)" &&
+		C="$(echo B..C | git pack-objects --revs $packdir/pack)" &&
+		D="$(echo B..D | git pack-objects --revs $packdir/pack)" &&
+
+		C_ONLY="$(git rev-parse other | git pack-objects $packdir/pack)" &&
+
+		git prune-packed &&
+
+		# Create a pack using --stdin-packs=follow where:
+		#
+		#  - pack D is included,
+		#  - pack C_ONLY is excluded, but open,
+		#  - pack B is excluded, but closed, and
+		#  - packs A and C are unknown
+		#
+		# The resulting pack should therefore contain:
+		#
+		#  - objects from the included pack D,
+		#  - A.t (rescued via D^{tree}), and
+		#  - C^{tree} and C.t (rescued via pack C_ONLY)
+		#
+		# , but should omit:
+		#
+		#  - C (excluded via C_ONLY),
+		#  - objects from pack B (trivially excluded-closed)
+		#  - A and A^{tree} (ancestors of B)
+		P=$(git pack-objects --stdin-packs=follow $packdir/pack <<-EOF
+		pack-$D.pack
+		!pack-$C_ONLY.pack
+		^pack-$B.pack
+		EOF
+		) &&
+
+		{
+			objects_in_packs $D &&
+			git rev-parse A:A.t "C^{tree}" C:C.t
+		} >expect.raw &&
+		sort expect.raw >expect &&
+
+		objects_in_packs $P >actual &&
+		test_cmp expect actual
+	)
+'
+
+test_expect_success '--stdin-packs with !-delimited pack without follow' '
+	test_when_finished "rm -fr repo" &&
+
+	git init repo &&
+	(
+		test_commit A &&
+		test_commit B &&
+		test_commit C &&
+
+		A="$(echo A | git pack-objects --revs $packdir/pack)" &&
+		B="$(echo A..B | git pack-objects --revs $packdir/pack)" &&
+		C="$(echo B..C | git pack-objects --revs $packdir/pack)" &&
+
+		cat >in <<-EOF &&
+		!pack-$A.pack
+		pack-$B.pack
+		pack-$C.pack
+		EOF
+
+		# Without --stdin-packs=follow, we treat the first
+		# line of input as a literal packfile name, and thus
+		# expect pack-objects to complain of a missing pack
+		test_must_fail git pack-objects --stdin-packs --stdout \
+			>/dev/null <in 2>err &&
+		test_grep "could not find pack .!pack-$A.pack." err &&
+
+		# With --stdin-packs=follow, we treat the second line
+		# of input as indicating pack-$A.pack is an excluded
+		# open pack, and thus expect pack-objects to succeed
+		P=$(git pack-objects --stdin-packs=follow $packdir/pack <in) &&
+
+		objects_in_packs $B $C >expect &&
+		objects_in_packs $P >actual &&
+		test_cmp expect actual
+	)
+'
+
 test_done
diff --git a/t/t5333-pseudo-merge-bitmaps.sh b/t/t5333-pseudo-merge-bitmaps.sh
index 1f7a5d8..305d677 100755
--- a/t/t5333-pseudo-merge-bitmaps.sh
+++ b/t/t5333-pseudo-merge-bitmaps.sh
@@ -462,4 +462,233 @@
 	)
 '
 
+test_expect_success 'apply pseudo-merges during fill-in traversal' '
+	test_when_finished "rm -fr pseudo-merge-fill-in-traversal" &&
+	git init pseudo-merge-fill-in-traversal &&
+	(
+		cd pseudo-merge-fill-in-traversal &&
+
+		git config bitmapPseudoMerge.test.pattern refs/tags/ &&
+		git config bitmapPseudoMerge.test.maxMerges 1 &&
+		git config bitmapPseudoMerge.test.stableThreshold never &&
+
+		test_commit_bulk 64 &&
+		tag_everything &&
+		git repack -ad &&
+
+		pack=$(ls .git/objects/pack/pack-*.pack) &&
+		git rev-parse HEAD~63 >in &&
+		test-tool bitmap write "$(basename $pack)" <in &&
+
+		test_pseudo_merges >merges &&
+		test_line_count = 1 merges &&
+
+		test_commit stale &&
+
+		git rev-list --count --objects HEAD >expect &&
+
+		: >trace2.txt &&
+		GIT_TRACE2_EVENT=$PWD/trace2.txt \
+			git rev-list --count --objects --use-bitmap-index HEAD >actual &&
+		test_pseudo_merges_satisfied 1 <trace2.txt &&
+
+		test_cmp expect actual
+	)
+'
+
+test_expect_success 'apply pseudo-merges from multiple groups during fill-in' '
+	test_when_finished "rm -fr pseudo-merge-fill-in-multi" &&
+	git init pseudo-merge-fill-in-multi &&
+	(
+		cd pseudo-merge-fill-in-multi &&
+
+		test_commit base &&
+		base=$(git rev-parse HEAD) &&
+
+		for side in left right
+		do
+			git checkout -B $side base &&
+
+			test_commit_bulk --id=$side 64 &&
+			git rev-list --no-object-names HEAD --not $base >in &&
+			while read oid
+			do
+				echo "create refs/group-$side/$oid $oid" || return 1
+			done <in | git update-ref --stdin || return 1
+		done &&
+
+		git checkout left &&
+		git merge right &&
+		git repack -ad &&
+
+		git config bitmapPseudoMerge.left.pattern "refs/group-left/" &&
+		git config bitmapPseudoMerge.left.maxMerges 1 &&
+		git config bitmapPseudoMerge.left.stableThreshold never &&
+
+		git config bitmapPseudoMerge.right.pattern "refs/group-right/" &&
+		git config bitmapPseudoMerge.right.maxMerges 1 &&
+		git config bitmapPseudoMerge.right.stableThreshold never &&
+
+		pack="$(ls .git/objects/pack/pack-*.pack)" &&
+		git rev-parse "$base" >in &&
+		test-tool bitmap write "$(basename $pack)" <in &&
+
+		test_pseudo_merges >merges &&
+		test_line_count = 2 merges &&
+
+		test_commit stale &&
+
+		git rev-list --count --objects HEAD >expect &&
+
+		: >trace2.txt &&
+		GIT_TRACE2_EVENT=$PWD/trace2.txt \
+			git rev-list --count --objects --use-bitmap-index HEAD >actual &&
+		test_pseudo_merges_satisfied 2 <trace2.txt &&
+
+		test_cmp expect actual
+	)
+'
+
+test_expect_success 'apply pseudo-merges with overlapping groups during fill-in' '
+	test_when_finished "rm -fr pseudo-merge-fill-in-overlap" &&
+	git init pseudo-merge-fill-in-overlap &&
+	(
+		cd pseudo-merge-fill-in-overlap &&
+
+		test_commit_bulk 64 &&
+		tag_everything &&
+		git repack -ad &&
+
+		pack="$(ls .git/objects/pack/pack-*.pack)" &&
+
+		# Use two pseudo-merge group patterns that both match
+		# refs/tags/, so every tagged commit belongs to both
+		# groups. This exercises the extended lookup table
+		# path in apply_pseudo_merges_for_commit().
+		git config bitmapPseudoMerge.all.pattern "refs/tags/" &&
+		git config bitmapPseudoMerge.all.maxMerges 1 &&
+		git config bitmapPseudoMerge.all.stableThreshold never &&
+
+		git config bitmapPseudoMerge.tags.pattern "refs/tags/" &&
+		git config bitmapPseudoMerge.tags.maxMerges 1 &&
+		git config bitmapPseudoMerge.tags.stableThreshold never &&
+
+		git rev-parse HEAD~63 >in &&
+		test-tool bitmap write "$(basename $pack)" <in &&
+
+		test_pseudo_merges >merges &&
+		test_line_count = 2 merges &&
+
+		test_commit stale &&
+
+		git rev-list --count --objects HEAD >expect &&
+
+		: >trace2.txt &&
+		GIT_TRACE2_EVENT=$PWD/trace2.txt \
+			git rev-list --count --objects --use-bitmap-index HEAD >actual &&
+		test_pseudo_merges_satisfied 2 <trace2.txt &&
+
+		test_cmp expect actual
+	)
+'
+
+test_expect_success 'pseudo-merge commits are correctly classified by date' '
+	test_when_finished "rm -fr pseudo-merge-date-classification" &&
+	git init pseudo-merge-date-classification &&
+	(
+		cd pseudo-merge-date-classification &&
+
+		test_commit_bulk 64 &&
+
+		tag_everything &&
+		git repack -ad &&
+
+		pack="$(ls .git/objects/pack/pack-*.pack)" &&
+
+		# Configure two pseudo-merge groups: one that only
+		# matches "stable" refs (older than one month), and
+		# one that matches all refs. With 64 tags whose
+		# commits are all younger than one month, the
+		# "stable" group should have zero pseudo-merges and
+		# the "all" group should have one.
+		#
+		# Use GIT_TEST_DATE_NOW to align "now" (and therefore
+		# "1.month.ago") with the test_tick timestamps so that
+		# the commits are within the last month.
+		#
+		# Without parsing the commit, its date field would
+		# be zero, causing it to satisfy date <= threshold
+		# for the "stable" group as well, and both groups
+		# would produce pseudo-merges.
+		git config bitmapPseudoMerge.stable.pattern "refs/tags/" &&
+		git config bitmapPseudoMerge.stable.maxMerges 64 &&
+		git config bitmapPseudoMerge.stable.stableThreshold never &&
+		git config bitmapPseudoMerge.stable.threshold 1.month.ago &&
+
+		git config bitmapPseudoMerge.all.pattern "refs/tags/" &&
+		git config bitmapPseudoMerge.all.maxMerges 1 &&
+		git config bitmapPseudoMerge.all.stableThreshold never &&
+		git config bitmapPseudoMerge.all.threshold now &&
+
+		git rev-parse HEAD~63 >in &&
+		GIT_TEST_DATE_NOW=$test_tick \
+			test-tool bitmap write "$(basename $pack)" <in &&
+
+		test_pseudo_merges >merges &&
+		test_line_count = 1 merges
+	)
+'
+
+test_expect_success 'sampleRate=0 does not cause division by zero' '
+	test_when_finished "rm -fr pseudo-merge-sample-rate-zero" &&
+	git init pseudo-merge-sample-rate-zero &&
+	(
+		cd pseudo-merge-sample-rate-zero &&
+
+		test_commit_bulk 64 &&
+		tag_everything &&
+		git repack -ad &&
+
+		pack="$(ls .git/objects/pack/pack-*.pack)" &&
+
+		git config bitmapPseudoMerge.test.pattern "refs/tags/" &&
+		git config bitmapPseudoMerge.test.maxMerges 1 &&
+		git config bitmapPseudoMerge.test.sampleRate 0 &&
+		git config bitmapPseudoMerge.test.threshold now &&
+		git config bitmapPseudoMerge.test.stableThreshold never &&
+
+		git rev-parse HEAD~63 >in &&
+		test-tool bitmap write "$(basename $pack)" <in
+	)
+'
+
+test_expect_success 'duplicate pseudo-merge pattern does not leak' '
+	test_when_finished "rm -fr pseudo-merge-dup-pattern" &&
+	git init pseudo-merge-dup-pattern &&
+	(
+		cd pseudo-merge-dup-pattern &&
+
+		test_commit_bulk 64 &&
+		tag_everything &&
+		git repack -ad &&
+
+		pack=$(ls .git/objects/pack/pack-*.pack) &&
+
+		# Set the same group'\''s pattern twice. The second
+		# assignment should cleanly release the compiled regex
+		# from the first without leaking.
+		git config bitmapPseudoMerge.test.pattern "refs/tags/" &&
+		git config --add bitmapPseudoMerge.test.pattern "refs/tags/" &&
+		git config bitmapPseudoMerge.test.maxMerges 1 &&
+		git config bitmapPseudoMerge.test.threshold now &&
+		git config bitmapPseudoMerge.test.stableThreshold never &&
+
+		git rev-parse HEAD~63 >in &&
+		test-tool bitmap write "$(basename $pack)" <in &&
+
+		test_pseudo_merges >merges &&
+		test_line_count = 1 merges
+	)
+'
+
 test_done
diff --git a/t/t5334-incremental-multi-pack-index.sh b/t/t5334-incremental-multi-pack-index.sh
index 99c7d44..68a103d 100755
--- a/t/t5334-incremental-multi-pack-index.sh
+++ b/t/t5334-incremental-multi-pack-index.sh
@@ -96,6 +96,53 @@
 	git cat-file -p 2.2
 '
 
+test_expect_success 'write MIDX layer with --no-write-chain-file' '
+	test_commit no-write-chain-file &&
+	git repack -d &&
+
+	cp "$midx_chain" "$midx_chain.bak" &&
+	layer="$(git multi-pack-index write --bitmap --incremental \
+		--no-write-chain-file)" &&
+
+	test_cmp "$midx_chain.bak" "$midx_chain" &&
+	test_path_is_file "$midxdir/multi-pack-index-$layer.midx"
+'
+
+test_expect_success 'write non-incremental MIDX layer with --no-write-chain-file' '
+	test_must_fail git multi-pack-index write --bitmap --no-write-chain-file 2>err &&
+	test_grep "cannot use --no-write-chain-file without --incremental" err
+'
+
+test_expect_success 'write MIDX layer with --base without --no-write-chain-file' '
+	test_must_fail git multi-pack-index write --bitmap --incremental \
+		--base=none 2>err &&
+	test_grep "cannot use --base without --no-write-chain-file" err
+'
+
+test_expect_success 'write MIDX layer with --base=none and --no-write-chain-file' '
+	test_commit base-none &&
+	git repack -d &&
+
+	cp "$midx_chain" "$midx_chain.bak" &&
+	layer="$(git multi-pack-index write --bitmap --incremental \
+		--no-write-chain-file --base=none)" &&
+
+	test_cmp "$midx_chain.bak" "$midx_chain" &&
+	test_path_is_file "$midxdir/multi-pack-index-$layer.midx"
+'
+
+test_expect_success 'write MIDX layer with --base=<hash> and --no-write-chain-file' '
+	test_commit base-hash &&
+	git repack -d &&
+
+	cp "$midx_chain" "$midx_chain.bak" &&
+	layer="$(git multi-pack-index write --bitmap --incremental \
+		--no-write-chain-file --base="$(nth_line 1 "$midx_chain")")" &&
+
+	test_cmp "$midx_chain.bak" "$midx_chain" &&
+	test_path_is_file "$midxdir/multi-pack-index-$layer.midx"
+'
+
 for reuse in false single multi
 do
 	test_expect_success "full clone (pack.allowPackReuse=$reuse)" '
@@ -132,4 +179,20 @@
 
 '
 
+test_expect_success 'non-incremental write with existing incremental chain' '
+	git init non-incremental-write-with-existing &&
+	test_when_finished "rm -fr non-incremental-write-with-existing" &&
+
+	(
+		cd non-incremental-write-with-existing &&
+
+		git config set maintenance.auto false &&
+
+		write_midx_layer &&
+		write_midx_layer &&
+
+		git multi-pack-index write
+	)
+'
+
 test_done
diff --git a/t/t5335-compact-multi-pack-index.sh b/t/t5335-compact-multi-pack-index.sh
index 40f3844..ec1dafe 100755
--- a/t/t5335-compact-multi-pack-index.sh
+++ b/t/t5335-compact-multi-pack-index.sh
@@ -290,4 +290,117 @@
 	)
 '
 
+test_expect_success 'MIDX compaction with --no-write-chain-file' '
+	git init midx-compact-with--no-write-chain-file &&
+	(
+		cd midx-compact-with--no-write-chain-file &&
+
+		git config maintenance.auto false &&
+
+		write_packs A B C D &&
+
+		test_line_count = 4 $midx_chain &&
+		cp "$midx_chain" "$midx_chain".bak &&
+
+		layer="$(git multi-pack-index compact --incremental \
+			--no-write-chain-file \
+			--base="$(nth_line 1 "$midx_chain")" \
+			"$(nth_line 2 "$midx_chain")" \
+			"$(nth_line 3 "$midx_chain")")" &&
+
+		test_cmp "$midx_chain.bak" "$midx_chain" &&
+
+		# After writing the new layer, insert it into the chain
+		# manually. This is done in order to make $layer visible
+		# to the read-midx test helper below, and matches what
+		# the MIDX command would do without --no-write-chain-file.
+		{
+			nth_line 1 "$midx_chain.bak" &&
+			echo $layer &&
+			nth_line 4 "$midx_chain.bak"
+		} >$midx_chain &&
+
+		test-tool read-midx $objdir $layer >midx.data &&
+		grep "^pack-B-.*\.idx" midx.data &&
+		grep "^pack-C-.*\.idx" midx.data
+
+	)
+'
+
+test_expect_success 'MIDX compaction with --base' '
+	git init midx-compact-with--base &&
+	(
+		cd midx-compact-with--base &&
+
+		git config maintenance.auto false &&
+
+		write_packs A B C D &&
+
+		test_line_count = 4 "$midx_chain" &&
+
+		cp "$midx_chain" "$midx_chain.bak" &&
+
+		git multi-pack-index compact --incremental \
+			--base="$(nth_line 1 "$midx_chain")" \
+			"$(nth_line 3 "$midx_chain")" \
+			"$(nth_line 4 "$midx_chain")" &&
+		test_line_count = 2 $midx_chain &&
+
+		nth_line 1 "$midx_chain.bak" >expect &&
+		nth_line 1 "$midx_chain" >actual &&
+
+		test_cmp expect actual
+	)
+'
+
+test_expect_success 'MIDX compaction with --base=none' '
+	git init midx-compact-base-none &&
+	(
+		cd midx-compact-base-none &&
+
+		git config maintenance.auto false &&
+
+		write_packs A B C D &&
+
+		test_line_count = 4 $midx_chain &&
+
+		cp "$midx_chain" "$midx_chain".bak &&
+
+		# Compact the two bottommost layers (A and B) into a new
+		# root layer with no parent.
+		git multi-pack-index compact --incremental \
+			--base=none \
+			"$(nth_line 1 "$midx_chain")" \
+			"$(nth_line 2 "$midx_chain")" &&
+
+		test_line_count = 3 $midx_chain &&
+
+		# The upper layers (C and D) should be preserved
+		# unchanged.
+		nth_line 3 "$midx_chain.bak" >expect &&
+		nth_line 4 "$midx_chain.bak" >>expect &&
+		nth_line 2 "$midx_chain" >actual &&
+		nth_line 3 "$midx_chain" >>actual &&
+
+		test_cmp expect actual
+	)
+'
+
+test_expect_success 'MIDX compaction with bogus --base checksum' '
+	git init midx-compact-bogus-base &&
+	(
+		cd midx-compact-bogus-base &&
+
+		git config maintenance.auto false &&
+
+		write_packs A B C &&
+
+		test_must_fail git multi-pack-index compact --incremental \
+			--base=deadbeef \
+			"$(nth_line 2 "$midx_chain")" \
+			"$(nth_line 3 "$midx_chain")" 2>err &&
+		test_grep "could not find base MIDX" err
+	)
+'
+
 test_done
diff --git a/t/t5409-colorize-remote-messages.sh b/t/t5409-colorize-remote-messages.sh
index fa5de45..3010913 100755
--- a/t/t5409-colorize-remote-messages.sh
+++ b/t/t5409-colorize-remote-messages.sh
@@ -98,4 +98,96 @@
 	grep "<BOLD;RED>error<RESET>: error" decoded
 '
 
+test_expect_success 'disallow (color) control sequences in sideband' '
+	write_script .git/color-me-surprised <<-\EOF &&
+	printf "error: Have you \\033[31mread\\033[m this?\\a\\n" >&2
+	exec "$@"
+	EOF
+	test_config_global uploadPack.packObjectsHook ./color-me-surprised &&
+	test_commit need-at-least-one-commit &&
+
+	git clone --no-local . throw-away 2>stderr &&
+	test_decode_color <stderr >decoded &&
+	test_grep RED decoded &&
+	test_grep "\\^G" stderr &&
+	tr -dc "\\007" <stderr >actual &&
+	test_must_be_empty actual &&
+
+	rm -rf throw-away &&
+	git -c sideband.allowControlCharacters=false \
+		clone --no-local . throw-away 2>stderr &&
+	test_decode_color <stderr >decoded &&
+	test_grep ! RED decoded &&
+	test_grep "\\^G" stderr &&
+
+	rm -rf throw-away &&
+	git -c sideband.allowControlCharacters clone --no-local . throw-away 2>stderr &&
+	test_decode_color <stderr >decoded &&
+	test_grep RED decoded &&
+	tr -dc "\\007" <stderr >actual &&
+	test_file_not_empty actual
+'
+
+test_decode_csi() {
+	awk '{
+		while (match($0, /\033/) != 0) {
+			printf "%sCSI ", substr($0, 1, RSTART-1);
+			$0 = substr($0, RSTART + RLENGTH, length($0) - RSTART - RLENGTH + 1);
+		}
+		print
+	}'
+}
+
+test_expect_success 'control sequences in sideband allowed by default' '
+	write_script .git/color-me-surprised <<-\EOF &&
+	printf "error: \\033[31mcolor\\033[m\\033[Goverwrite\\033[Gerase\\033[K\\033?25l\\n" >&2
+	exec "$@"
+	EOF
+	test_config_global uploadPack.packObjectsHook ./color-me-surprised &&
+	test_commit need-at-least-one-commit-at-least &&
+
+	rm -rf throw-away &&
+	git clone --no-local . throw-away 2>stderr &&
+	test_decode_color <stderr >color-decoded &&
+	test_decode_csi <color-decoded >decoded &&
+	test_grep ! "CSI \\[K" decoded &&
+	test_grep ! "CSI \\[G" decoded &&
+	test_grep "\\^\\[?25l" decoded &&
+
+	rm -rf throw-away &&
+	git -c sideband.allowControlCharacters=erase,cursor,color \
+		clone --no-local . throw-away 2>stderr &&
+	test_decode_color <stderr >color-decoded &&
+	test_decode_csi <color-decoded >decoded &&
+	test_grep "RED" decoded &&
+	test_grep "CSI \\[K" decoded &&
+	test_grep "CSI \\[G" decoded &&
+	test_grep ! "\\^\\[\\[K" decoded &&
+	test_grep ! "\\^\\[\\[G" decoded
+'
+
+test_expect_success 'allow all control sequences for a specific URL' '
+	write_script .git/eraser <<-\EOF &&
+	printf "error: Ohai!\\r\\033[K" >&2
+	exec "$@"
+	EOF
+	test_config_global uploadPack.packObjectsHook ./eraser &&
+	test_commit one-more-please &&
+
+	rm -rf throw-away &&
+	git clone --no-local . throw-away 2>stderr &&
+	test_decode_color <stderr >color-decoded &&
+	test_decode_csi <color-decoded >decoded &&
+	test_grep ! "CSI \\[K" decoded &&
+	test_grep "\\^\\[\\[K" decoded &&
+
+	rm -rf throw-away &&
+	git -c "sideband.file://.allowControlCharacters=true" \
+		clone --no-local "file://$PWD" throw-away 2>stderr &&
+	test_decode_color <stderr >color-decoded &&
+	test_decode_csi <color-decoded >decoded &&
+	test_grep "CSI \\[K" decoded &&
+	test_grep ! "\\^\\[\\[K" decoded
+'
+
 test_done
diff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh
index 6fe21e2..eca9a97 100755
--- a/t/t5510-fetch.sh
+++ b/t/t5510-fetch.sh
@@ -1465,6 +1465,197 @@
 	test_cmp fatal-expect fatal-actual
 '
 
+test_expect_success '--negotiation-tip ignores missing refs and invalid hashes' '
+	setup_negotiation_tip server server 0 &&
+	GIT_TRACE_PACKET="$(pwd)/trace" git -C client fetch \
+		--negotiation-tip=alpha_1 --negotiation-tip=beta_1 \
+		--negotiation-tip=no-such-ref \
+		--negotiation-tip=invalid-hash \
+		origin alpha_s beta_s &&
+	check_negotiation_tip
+'
+
+test_expect_success '--negotiation-restrict limits "have" lines sent' '
+	setup_negotiation_tip server server 0 &&
+	GIT_TRACE_PACKET="$(pwd)/trace" git -C client fetch \
+		--negotiation-restrict=alpha_1 --negotiation-restrict=beta_1 \
+		origin alpha_s beta_s &&
+	check_negotiation_tip
+'
+
+test_expect_success '--negotiation-restrict understands globs' '
+	setup_negotiation_tip server server 0 &&
+	GIT_TRACE_PACKET="$(pwd)/trace" git -C client fetch \
+		--negotiation-restrict=*_1 \
+		origin alpha_s beta_s &&
+	check_negotiation_tip
+'
+
+test_expect_success '--negotiation-restrict and --negotiation-tip can be mixed' '
+	setup_negotiation_tip server server 0 &&
+	GIT_TRACE_PACKET="$(pwd)/trace" git -C client fetch \
+		--negotiation-restrict=alpha_1 \
+		--negotiation-tip=beta_1 \
+		origin alpha_s beta_s &&
+	check_negotiation_tip
+'
+
+test_expect_success 'remote.<name>.negotiationRestrict used as default' '
+	setup_negotiation_tip server server 0 &&
+
+	# test the reset of the list on an empty value
+	git -C client config --add remote.origin.negotiationRestrict alpha_2 &&
+	git -C client config --add remote.origin.negotiationRestrict "" &&
+	git -C client config --add remote.origin.negotiationRestrict alpha_1 &&
+	git -C client config --add remote.origin.negotiationRestrict beta_1 &&
+	GIT_TRACE_PACKET="$(pwd)/trace" git -C client fetch \
+		origin alpha_s beta_s &&
+	check_negotiation_tip
+'
+
+test_expect_success 'CLI --negotiation-restrict overrides remote config' '
+	setup_negotiation_tip server server 0 &&
+	git -C client config --add remote.origin.negotiationRestrict alpha_1 &&
+	git -C client config --add remote.origin.negotiationRestrict beta_1 &&
+	ALPHA_1=$(git -C client rev-parse alpha_1) &&
+	GIT_TRACE_PACKET="$(pwd)/trace" git -C client fetch \
+		--negotiation-restrict=alpha_1 \
+		origin alpha_s beta_s &&
+	test_grep "fetch> have $ALPHA_1" trace &&
+	BETA_1=$(git -C client rev-parse beta_1) &&
+	test_grep ! "fetch> have $BETA_1" trace
+'
+
+test_expect_success '--negotiation-include includes configured refs as haves' '
+	test_when_finished rm -f trace &&
+	setup_negotiation_tip server server 0 &&
+
+	GIT_TRACE_PACKET="$(pwd)/trace" git -C client fetch \
+		--negotiation-restrict=alpha_1 \
+		--negotiation-include=refs/tags/beta_1 \
+		origin alpha_s beta_s &&
+
+	ALPHA_1=$(git -C client rev-parse alpha_1) &&
+	test_grep "fetch> have $ALPHA_1" trace &&
+	BETA_1=$(git -C client rev-parse beta_1) &&
+	test_grep "fetch> have $BETA_1" trace
+'
+
+test_expect_success '--negotiation-include works with glob patterns' '
+	test_when_finished rm -f trace &&
+	setup_negotiation_tip server server 0 &&
+
+	GIT_TRACE_PACKET="$(pwd)/trace" git -C client fetch \
+		--negotiation-restrict=alpha_1 \
+		--negotiation-include="refs/tags/beta_*" \
+		origin alpha_s beta_s &&
+
+	BETA_1=$(git -C client rev-parse beta_1) &&
+	test_grep "fetch> have $BETA_1" trace &&
+	BETA_2=$(git -C client rev-parse beta_2) &&
+	test_grep "fetch> have $BETA_2" trace
+'
+
+test_expect_success '--negotiation-include is additive with negotiation' '
+	test_when_finished rm -f trace &&
+	setup_negotiation_tip server server 0 &&
+
+	GIT_TRACE_PACKET="$(pwd)/trace" git -C client fetch \
+		--negotiation-include=refs/tags/beta_1 \
+		origin alpha_s beta_s &&
+
+	BETA_1=$(git -C client rev-parse beta_1) &&
+	test_grep "fetch> have $BETA_1" trace
+'
+
+test_expect_success '--negotiation-include ignores non-existent refs silently' '
+	setup_negotiation_tip server server 0 &&
+
+	git -C client fetch --quiet \
+		--negotiation-restrict=alpha_1 \
+		--negotiation-include=refs/tags/nonexistent \
+		origin alpha_s beta_s 2>err &&
+	test_must_be_empty err
+'
+
+test_expect_success '--negotiation-include avoids duplicates with negotiator' '
+	test_when_finished rm -f trace &&
+	setup_negotiation_tip server server 0 &&
+
+	ALPHA_1=$(git -C client rev-parse alpha_1) &&
+	GIT_TRACE_PACKET="$(pwd)/trace" git -C client fetch \
+		--negotiation-restrict=alpha_1 \
+		--negotiation-include=refs/tags/alpha_1 \
+		origin alpha_s beta_s &&
+
+	test_grep "fetch> have $ALPHA_1" trace >matches &&
+	test_line_count = 1 matches
+'
+
+test_expect_success 'remote.<name>.negotiationInclude used as default for --negotiation-include' '
+	test_when_finished rm -f trace &&
+	setup_negotiation_tip server server 0 &&
+
+	# test the reset of the list on an empty value
+	git -C client config --add remote.origin.negotiationInclude refs/tags/alpha_1 &&
+	git -C client config --add remote.origin.negotiationInclude "" &&
+	git -C client config --add remote.origin.negotiationInclude refs/tags/beta_1 &&
+	GIT_TRACE_PACKET="$(pwd)/trace" git -C client fetch \
+		--negotiation-restrict=beta_2 \
+		origin alpha_s beta_s &&
+
+	ALPHA_1=$(git -C client rev-parse alpha_1) &&
+	test_grep ! "fetch> have $ALPHA_1" trace &&
+	BETA_1=$(git -C client rev-parse beta_1) &&
+	test_grep "fetch> have $BETA_1" trace
+'
+
+test_expect_success 'remote.<name>.negotiationInclude works with glob patterns' '
+	test_when_finished rm -f trace &&
+	setup_negotiation_tip server server 0 &&
+
+	git -C client config --add remote.origin.negotiationInclude "refs/tags/beta_*" &&
+	GIT_TRACE_PACKET="$(pwd)/trace" git -C client fetch \
+		--negotiation-restrict=alpha_1 \
+		origin alpha_s beta_s &&
+
+	BETA_1=$(git -C client rev-parse beta_1) &&
+	test_grep "fetch> have $BETA_1" trace &&
+	BETA_2=$(git -C client rev-parse beta_2) &&
+	test_grep "fetch> have $BETA_2" trace
+'
+
+test_expect_success 'CLI --negotiation-include overrides remote.<name>.negotiationInclude' '
+	test_when_finished rm -f trace &&
+	setup_negotiation_tip server server 0 &&
+
+	git -C client config --add remote.origin.negotiationInclude refs/tags/beta_2 &&
+	GIT_TRACE_PACKET="$(pwd)/trace" git -C client fetch \
+		--negotiation-restrict=alpha_1 \
+		--negotiation-include=refs/tags/beta_1 \
+		origin alpha_s beta_s &&
+
+	BETA_1=$(git -C client rev-parse beta_1) &&
+	test_grep "fetch> have $BETA_1" trace &&
+	BETA_2=$(git -C client rev-parse beta_2) &&
+	test_grep ! "fetch> have $BETA_2" trace
+'
+
+test_expect_success '--negotiation-include avoids duplicates with v0' '
+	test_when_finished rm -f trace &&
+	setup_negotiation_tip server server 0 &&
+
+	ALPHA_1=$(git -C client rev-parse alpha_1) &&
+	GIT_TRACE_PACKET="$(pwd)/trace" git -C client \
+		-c protocol.version=0 fetch \
+		--negotiation-restrict=alpha_1 \
+		--negotiation-include=refs/tags/alpha_1 \
+		origin alpha_s beta_s &&
+
+	test_grep "fetch> have $ALPHA_1" trace >matches &&
+	test_line_count = 1 matches
+'
+
 test_expect_success SYMLINKS 'clone does not get confused by a D/F conflict' '
 	git init df-conflict &&
 	(
diff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh
index 29e2f17..1b98634 100755
--- a/t/t5516-fetch-push.sh
+++ b/t/t5516-fetch-push.sh
@@ -254,6 +254,36 @@
 	! grep "Fetching submodule" err
 '
 
+test_expect_success 'push with negotiation and remote.<name>.negotiationInclude' '
+	test_when_finished rm -rf negotiation_include &&
+	mk_empty negotiation_include &&
+	git push negotiation_include $the_first_commit:refs/remotes/origin/first_commit &&
+	test_commit -C negotiation_include unrelated_commit &&
+	git -C negotiation_include config receive.hideRefs refs/remotes/origin/first_commit &&
+	test_when_finished "rm event" &&
+	GIT_TRACE2_EVENT="$(pwd)/event" \
+		git -c protocol.version=2 -c push.negotiate=1 \
+		-c remote.negotiation_include.negotiationInclude=refs/heads/main \
+		push negotiation_include refs/heads/main:refs/remotes/origin/main &&
+	test_grep \"key\":\"total_rounds\" event &&
+	grep_wrote 2 event # 1 commit, 1 tree
+'
+
+test_expect_success 'push with negotiation and remote.<name>.negotiationRestrict' '
+	test_when_finished rm -rf negotiation_restrict &&
+	mk_empty negotiation_restrict &&
+	git push negotiation_restrict $the_first_commit:refs/remotes/origin/first_commit &&
+	test_commit -C negotiation_restrict unrelated_commit &&
+	git -C negotiation_restrict config receive.hideRefs refs/remotes/origin/first_commit &&
+	test_when_finished "rm event" &&
+	GIT_TRACE2_EVENT="$(pwd)/event" \
+		git -c protocol.version=2 -c push.negotiate=1 \
+		-c remote.negotiation_restrict.negotiationRestrict=refs/heads/main \
+		push negotiation_restrict refs/heads/main:refs/remotes/origin/main &&
+	test_grep \"key\":\"total_rounds\" event &&
+	grep_wrote 2 event # 1 commit, 1 tree
+'
+
 test_expect_success 'push without wildcard' '
 	mk_empty testrepo &&
 
@@ -1349,7 +1379,7 @@
 		git for-each-ref >tmp1 &&
 		sed -n "p; s|refs/heads/main$|refs/remotes/origin/main|p" tmp1 |
 		sed -n "p; s|refs/heads/main$|refs/remotes/origin/HEAD|p"  |
-		sort -k 4 >../expect
+		sort -k 3 >../expect
 	) &&
 	test_when_finished "rm -rf dst" &&
 	git init dst &&
@@ -1791,7 +1821,19 @@
 	)
 '
 
+test_expect_success 'denyCurrentBranch and core.worktree' '
+	test_when_finished "rm -fr cloned cloned.git" &&
+	git clone --separate-git-dir cloned.git . cloned &&
+	git --git-dir cloned.git config receive.denyCurrentBranch updateInstead &&
+	git --git-dir cloned.git config core.worktree "$PWD/cloned" &&
+	test_commit raspberry &&
+	git push cloned.git HEAD:main &&
+	test_path_exists cloned/raspberry.t &&
+	test_must_fail git push --delete cloned.git main
+'
+
 test_expect_success 'denyCurrentBranch and worktrees' '
+	test_when_finished "rm -fr cloned && git worktree remove --force new-wt" &&
 	git worktree add new-wt &&
 	git clone . cloned &&
 	test_commit -C cloned first &&
@@ -1816,6 +1858,20 @@
 	test_must_fail git push --delete bare.git wt
 '
 
+test_expect_success 'updateInstead with bare repository worktree and unborn bare HEAD' '
+	test_when_finished "rm -fr bare.git cloned" &&
+	git clone --bare . bare.git &&
+	git -C bare.git worktree add wt &&
+	git -C bare.git config receive.denyCurrentBranch updateInstead &&
+	git -C bare.git symbolic-ref HEAD refs/heads/unborn &&
+	test_must_fail git -C bare.git rev-parse -q --verify HEAD^{commit} &&
+	git clone . cloned &&
+	test_commit -C cloned mozzarella &&
+	git -C cloned push ../bare.git HEAD:wt &&
+	test_path_exists bare.git/wt/mozzarella.t &&
+	test "$(git -C cloned rev-parse HEAD)" = "$(git -C bare.git/wt rev-parse HEAD)"
+'
+
 test_expect_success 'refuse fetch to current branch of worktree' '
 	test_when_finished "git worktree remove --force wt && git branch -D wt" &&
 	git worktree add wt &&
diff --git a/t/t5537-fetch-shallow.sh b/t/t5537-fetch-shallow.sh
index 6588ce6..9982dd2 100755
--- a/t/t5537-fetch-shallow.sh
+++ b/t/t5537-fetch-shallow.sh
@@ -251,6 +251,16 @@
 		origin "+refs/heads/*:refs/remotes/origin/*"
 '
 
+test_expect_success 'fetch --deepen does not truncate' '
+	git clone --no-local .git full-clone &&
+	git -C full-clone rev-parse --is-shallow-repository >expect &&
+	git -C full-clone log --oneline >>expect &&
+	git -C full-clone fetch --deepen=1 &&
+	git -C full-clone rev-parse --is-shallow-repository >actual &&
+	git -C full-clone log --oneline >>actual &&
+	test_cmp expect actual
+'
+
 . "$TEST_DIRECTORY"/lib-httpd.sh
 start_httpd
 
diff --git a/t/t5550-http-fetch-dumb.sh b/t/t5550-http-fetch-dumb.sh
index 9d0a7f5..b0080bf 100755
--- a/t/t5550-http-fetch-dumb.sh
+++ b/t/t5550-http-fetch-dumb.sh
@@ -555,4 +555,24 @@
 	git -C idx-v1 fsck
 '
 
+test_expect_success 'absolute-path alternate when url has no path' '
+	src=$HTTPD_DOCUMENT_ROOT_PATH/repo.git &&
+	alt=absolute-alt.git &&
+	git clone --bare --shared "$src" "$alt" &&
+
+	# Our repo has an alternate pointing to the absolute filesystem path,
+	# but that will not make any sense to an http client. So we will
+	# manually give it the equivalent path that the http server will
+	# understand.
+	echo "/dumb/repo.git/objects" >"$alt/objects/info/http-alternates" &&
+
+	# Now make our alt repository available at the root of the http
+	# server without any path (i.e., just http://localhost:1234).
+	git -C "$alt" update-server-info &&
+	mv absolute-alt.git/* "$HTTPD_DOCUMENT_ROOT_PATH" &&
+
+	git -c http.followRedirects=true clone "$HTTPD_URL" alt-clone.git 2>err &&
+	test_grep "adding alternate object store: $HTTPD_URL/dumb/repo.git" err
+'
+
 test_done
diff --git a/t/t5551-http-fetch-smart.sh b/t/t5551-http-fetch-smart.sh
index a26b6c2..e236e52 100755
--- a/t/t5551-http-fetch-smart.sh
+++ b/t/t5551-http-fetch-smart.sh
@@ -481,6 +481,7 @@
 '
 
 test_expect_success EXPENSIVE 'http can handle enormous ref negotiation' '
+	test_when_finished "rm -f tags" &&
 	(
 		cd "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
 		create_tags 2001 50000
diff --git a/t/t5563-simple-http-auth.sh b/t/t5563-simple-http-auth.sh
index 0063581..a7d475d 100755
--- a/t/t5563-simple-http-auth.sh
+++ b/t/t5563-simple-http-auth.sh
@@ -719,4 +719,78 @@
 	EOF
 '
 
+test_lazy_prereq SPNEGO 'curl --version | grep -qi "SPNEGO\|GSS-API\|Kerberos\|negotiate"'
+
+test_expect_success SPNEGO 'http.emptyAuth=auto attempts Negotiate before credential_fill' '
+	test_when_finished "per_test_cleanup" &&
+
+	set_credential_reply get <<-EOF &&
+	username=alice
+	password=secret-passwd
+	EOF
+
+	# Basic base64(alice:secret-passwd)
+	cat >"$HTTPD_ROOT_PATH/custom-auth.valid" <<-EOF &&
+	id=1 creds=Basic YWxpY2U6c2VjcmV0LXBhc3N3ZA==
+	EOF
+
+	cat >"$HTTPD_ROOT_PATH/custom-auth.challenge" <<-EOF &&
+	id=1 status=200
+	id=default response=WWW-Authenticate: Negotiate
+	id=default response=WWW-Authenticate: Basic realm="example.com"
+	EOF
+
+	test_config_global credential.helper test-helper &&
+	GIT_TRACE_CURL="$TRASH_DIRECTORY/trace-auto" \
+		git -c http.emptyAuth=auto \
+		ls-remote "$HTTPD_URL/custom_auth/repo.git" &&
+
+	# In auto mode with a Negotiate+Basic server, there should be
+	# three 401 responses: (1) initial no-auth request, (2) empty-auth
+	# retry where Negotiate fails (no Kerberos ticket), (3) libcurl
+	# internal Negotiate retry. The fourth attempt uses Basic
+	# credentials from credential_fill and succeeds.
+	grep "HTTP/[0-9.]* 401" "$TRASH_DIRECTORY/trace-auto" >actual_401s &&
+	test_line_count = 3 actual_401s &&
+
+	expect_credential_query get <<-EOF
+	capability[]=authtype
+	capability[]=state
+	protocol=http
+	host=$HTTPD_DEST
+	wwwauth[]=Negotiate
+	wwwauth[]=Basic realm="example.com"
+	EOF
+'
+
+test_expect_success SPNEGO 'http.emptyAuth=false skips Negotiate' '
+	test_when_finished "per_test_cleanup" &&
+
+	set_credential_reply get <<-EOF &&
+	username=alice
+	password=secret-passwd
+	EOF
+
+	# Basic base64(alice:secret-passwd)
+	cat >"$HTTPD_ROOT_PATH/custom-auth.valid" <<-EOF &&
+	id=1 creds=Basic YWxpY2U6c2VjcmV0LXBhc3N3ZA==
+	EOF
+
+	cat >"$HTTPD_ROOT_PATH/custom-auth.challenge" <<-EOF &&
+	id=1 status=200
+	id=default response=WWW-Authenticate: Negotiate
+	id=default response=WWW-Authenticate: Basic realm="example.com"
+	EOF
+
+	test_config_global credential.helper test-helper &&
+	GIT_TRACE_CURL="$TRASH_DIRECTORY/trace-false" \
+		git -c http.emptyAuth=false \
+		ls-remote "$HTTPD_URL/custom_auth/repo.git" &&
+
+	# With emptyAuth=false, Negotiate is stripped immediately and
+	# credential_fill is called right away. Only one 401 response.
+	grep "HTTP/[0-9.]* 401" "$TRASH_DIRECTORY/trace-false" >actual_401s &&
+	test_line_count = 1 actual_401s
+'
+
 test_done
diff --git a/t/t5564-http-proxy.sh b/t/t5564-http-proxy.sh
index 3bcbdef..817cd1f 100755
--- a/t/t5564-http-proxy.sh
+++ b/t/t5564-http-proxy.sh
@@ -50,14 +50,20 @@
 
 # The %30 tests that the correct amount of percent-encoding is applied to the
 # proxy string passed to curl.
+# Use a short path for the socket to avoid exceeding the 108-character
+# Unix domain socket limit when the trash directory path is long.
+SOCKS_TMPDIR=$(mktemp -d)
+SOCKS_SOCK="$SOCKS_TMPDIR/%30.sock"
+
 test_lazy_prereq SOCKS_PROXY '
 	test_have_prereq PERL &&
-	start_socks "$TRASH_DIRECTORY/%30.sock"
+	start_socks "$SOCKS_SOCK"
 '
 
 test_atexit '
 	test ! -e "$TRASH_DIRECTORY/socks.pid" ||
 	kill "$(cat "$TRASH_DIRECTORY/socks.pid")"
+	rm -rf "$SOCKS_TMPDIR"
 '
 
 # The below tests morally ought to be gated on a prerequisite that Git is
@@ -70,7 +76,8 @@
 
 test_expect_success SOCKS_PROXY 'clone via Unix socket' '
 	test_when_finished "rm -rf clone" &&
-	test_config_global http.proxy "socks4://localhost$PWD/%2530.sock" && {
+	socks_proxy_url="socks4://localhost$(echo "$SOCKS_SOCK" | sed "s/%/%25/g")" &&
+	test_config_global http.proxy "$socks_proxy_url" && {
 		{
 			GIT_TRACE_CURL=$PWD/trace \
 			GIT_TRACE_CURL_COMPONENTS=socks \
@@ -95,4 +102,10 @@
 	}
 EOT
 
+test_expect_success 'unknown proxy scheme is rejected' '
+	test_must_fail git clone -c http.proxy=htpp://127.0.0.1 \
+		https://example.com/repo.git 2>err &&
+	test_grep "unsupported proxy scheme '\''htpp'\''" err
+'
+
 test_done
diff --git a/t/t5601-clone.sh b/t/t5601-clone.sh
index d743d98..3dd229c 100755
--- a/t/t5601-clone.sh
+++ b/t/t5601-clone.sh
@@ -813,7 +813,9 @@
 	test_when_finished "rm -rf repo \"$HTTPD_DOCUMENT_ROOT_PATH/repo.git\"" &&
 	git clone --bare --no-local src "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
 
-	test_when_finished "rm \"$HOME\"/.gitconfig" &&
+	test_when_finished "cp \"$HOME\"/.gitconfig.bak \
+		\"$HOME\"/.gitconfig 2>/dev/null || rm -f \"$HOME\"/.gitconfig" &&
+	cp "$HOME"/.gitconfig "$HOME"/.gitconfig.bak 2>/dev/null &&
 	cat >"$HOME"/.gitconfig <<-EOF &&
 	[includeIf "onbranch:something"]
 		path = /does/not/exist.inc
diff --git a/t/t5604-clone-reference.sh b/t/t5604-clone-reference.sh
index 470bfb6..c232ab8 100755
--- a/t/t5604-clone-reference.sh
+++ b/t/t5604-clone-reference.sh
@@ -360,4 +360,27 @@
 	grep "is a symlink, refusing to clone with --local" err
 '
 
+test_expect_success 'dissociate from repo with commit graph' '
+	git init orig &&
+	# We are trying to make sure the dissociated repo can
+	# find the tree of the tip commit, so the test could still
+	# serve its purpose with an empty tree. But having actual
+	# content future-proofs us against any kind of internal
+	# empty-tree optimizations.
+	echo content >orig/file &&
+	git -C orig add . &&
+	git -C orig commit -m foo &&
+
+	# We will use graph.git as our "local" source to dissociate
+	# from.
+	git clone --bare orig graph.git &&
+	git -C graph.git commit-graph write --reachable &&
+
+	# And then finally clone orig, using graph.git to get our objects. This
+	# must be non-bare so that we perform the checkout step, which will
+	# need to access the tree of HEAD, which we will have originally loaded
+	# via the commit graph.
+	git clone --no-local --reference graph.git --dissociate orig clone
+'
+
 test_done
diff --git a/t/t5608-clone-2gb.sh b/t/t5608-clone-2gb.sh
index 87a8cd9..4f8a95d 100755
--- a/t/t5608-clone-2gb.sh
+++ b/t/t5608-clone-2gb.sh
@@ -49,4 +49,41 @@
 
 '
 
+test_expect_success SIZE_T_IS_64BIT,EXPENSIVE 'set up repo with >4GB object' '
+	large_blob_size=$((4*1024*1024*1024+1)) &&
+	git init --bare 4gb-repo &&
+	head_oid=$(test-tool synthesize pack \
+		--reachable-large "$large_blob_size" \
+		4gb-repo/objects/pack/test.pack) &&
+	git -C 4gb-repo index-pack objects/pack/test.pack &&
+	git -C 4gb-repo update-ref refs/heads/main $head_oid &&
+	git -C 4gb-repo symbolic-ref HEAD refs/heads/main
+'
+
+test_expect_success SIZE_T_IS_64BIT,EXPENSIVE 'clone >4GB object via unpack-objects' '
+	# The synthesized pack has five objects, so a large unpack limit keeps
+	# fetch-pack on the unpack-objects path.
+	git -c fetch.unpackLimit=100 clone --bare \
+		"file://$(pwd)/4gb-repo" 4gb-clone-unpack &&
+
+	# Verify the large blob survived the clone by comparing its OID
+	# between source and clone.  We cannot use "cat-file -s" because
+	# object_info.sizep is still unsigned long, which truncates >4GB
+	# sizes on Windows.  OID equality proves content integrity since
+	# the clone already verified checksums via index-pack/unpack-objects.
+	source_blob=$(git -C 4gb-repo rev-parse main^:file) &&
+	clone_blob=$(git -C 4gb-clone-unpack rev-parse main^:file) &&
+	test "$source_blob" = "$clone_blob"
+'
+
+test_expect_success SIZE_T_IS_64BIT,EXPENSIVE 'clone with >4GB object via index-pack' '
+	# Force fetch-pack to hand the pack to index-pack instead.
+	git -c fetch.unpackLimit=1 clone --bare \
+		"file://$(pwd)/4gb-repo" 4gb-clone-index &&
+
+	source_blob=$(git -C 4gb-repo rev-parse main^:file) &&
+	clone_blob=$(git -C 4gb-clone-index rev-parse main^:file) &&
+	test "$source_blob" = "$clone_blob"
+'
+
 test_done
diff --git a/t/t5620-backfill.sh b/t/t5620-backfill.sh
index 2c347a9..d2ea68e 100755
--- a/t/t5620-backfill.sh
+++ b/t/t5620-backfill.sh
@@ -15,6 +15,14 @@
 	test_grep "unrecognized argument: --unexpected-arg" err
 '
 
+test_expect_success 'backfill rejects incompatible filter options' '
+	test_must_fail git backfill --objects --filter=tree:1 2>err &&
+	test_grep "cannot backfill with these filter options" err &&
+
+	test_must_fail git backfill --objects --filter=blob:limit=10m 2>err &&
+	test_grep "cannot backfill with blob size limits" err
+'
+
 # We create objects in the 'src' repo.
 test_expect_success 'setup repo for object creation' '
 	echo "{print \$1}" >print_1.awk &&
@@ -175,6 +183,21 @@
 	test_line_count = 0 missing
 '
 
+test_expect_success 'backfill auto-detects sparse-checkout from config' '
+	git clone --sparse --filter=blob:none \
+		--single-branch --branch=main \
+		"file://$(pwd)/srv.bare" backfill-auto-sparse &&
+
+	git -C backfill-auto-sparse rev-list --quiet --objects --missing=print HEAD >missing &&
+	test_line_count = 44 missing &&
+
+	GIT_TRACE2_EVENT="$(pwd)/auto-sparse-trace" git \
+		-C backfill-auto-sparse backfill &&
+
+	test_trace2_data promisor fetch_count 4 <auto-sparse-trace &&
+	test_trace2_data path-walk paths 5 <auto-sparse-trace
+'
+
 test_expect_success 'backfill --sparse without cone mode (positive)' '
 	git clone --no-checkout --filter=blob:none		\
 		--single-branch --branch=main 		\
@@ -242,11 +265,12 @@
 	git -C backfill-revs rev-list --quiet --objects --missing=print HEAD >missing &&
 	test_line_count = 48 missing &&
 
-	git -C backfill-revs backfill HEAD~2..HEAD &&
+	GIT_TRACE2_EVENT="$(pwd)/backfill-trace" git -C backfill-revs backfill HEAD~2..HEAD &&
 
-	# 30 objects downloaded.
+	# 36 objects downloaded, 12 still missing
+	test_trace2_data promisor fetch_count 36 <backfill-trace &&
 	git -C backfill-revs rev-list --quiet --objects --missing=print HEAD >missing &&
-	test_line_count = 18 missing
+	test_line_count = 12 missing
 '
 
 test_expect_success 'backfill with revisions over stdin' '
@@ -264,11 +288,12 @@
 	^HEAD~2
 	EOF
 
-	git -C backfill-revs backfill --stdin <in &&
+	GIT_TRACE2_EVENT="$(pwd)/backfill-trace" git -C backfill-revs backfill --stdin <in &&
 
-	# 30 objects downloaded.
+	# 36 objects downloaded, 12 still missing
+	test_trace2_data promisor fetch_count 36 <backfill-trace &&
 	git -C backfill-revs rev-list --quiet --objects --missing=print HEAD >missing &&
-	test_line_count = 18 missing
+	test_line_count = 12 missing
 '
 
 test_expect_success 'backfill with prefix pathspec' '
@@ -383,6 +408,102 @@
 	test_line_count = 6 missing
 '
 
+test_expect_success 'backfill range with include-edges enables fetch-free git-log' '
+	git clone --no-checkout --filter=blob:none	\
+		--single-branch --branch=main		\
+		"file://$(pwd)/srv.bare" backfill-log &&
+
+	# Backfill the range with default include edges.
+	git -C backfill-log backfill HEAD~2..HEAD &&
+
+	# git log -p needs edge blobs for the "before" side of
+	# diffs.  With edge inclusion, all needed blobs are local.
+	GIT_TRACE2_EVENT="$(pwd)/log-trace" git \
+		-C backfill-log log -p HEAD~2..HEAD >log-output &&
+
+	# No promisor fetches should have been needed.
+	! grep "fetch_count" log-trace
+'
+
+test_expect_success 'backfill range without include edges causes on-demand fetches in git-log' '
+	git clone --no-checkout --filter=blob:none	\
+		--single-branch --branch=main		\
+		"file://$(pwd)/srv.bare" backfill-log-no-bdy &&
+
+	# Backfill WITHOUT include edges -- file.3 v1 blobs are missing.
+	git -C backfill-log-no-bdy backfill --no-include-edges HEAD~2..HEAD &&
+
+	# git log -p HEAD~2..HEAD computes diff of commit 7 against
+	# commit 6.  It needs file.3 v1 (the "before" side), which was
+	# not backfilled.  This triggers on-demand promisor fetches.
+	GIT_TRACE2_EVENT="$(pwd)/log-no-bdy-trace" git \
+		-C backfill-log-no-bdy log -p HEAD~2..HEAD >log-output &&
+
+	grep "fetch_count" log-no-bdy-trace
+'
+
+test_expect_success 'backfill range enables fetch-free replay' '
+	# Create a repo with a branch to replay.
+	git init replay-src &&
+	(
+		cd replay-src &&
+		git config uploadpack.allowfilter 1 &&
+		git config uploadpack.allowanysha1inwant 1 &&
+		test_commit base &&
+		git checkout -b topic &&
+		test_commit topic-change &&
+		git checkout main &&
+		test_commit main-change
+	) &&
+	git clone --bare --filter=blob:none \
+		"file://$(pwd)/replay-src" replay-dest.git &&
+
+	# Backfill the replay range: --onto main, replaying topic~1..topic.
+	# For replay, we need TARGET^! plus the range.
+	main_oid=$(git -C replay-dest.git rev-parse main) &&
+	topic_oid=$(git -C replay-dest.git rev-parse topic) &&
+	base_oid=$(git -C replay-dest.git rev-parse topic~1) &&
+	git -C replay-dest.git backfill \
+		"$main_oid^!" "$base_oid..$topic_oid" &&
+
+	# Now replay should complete without any promisor fetches.
+	GIT_TRACE2_EVENT="$(pwd)/replay-trace" git -C replay-dest.git \
+		replay --onto main topic~1..topic >replay-out &&
+
+	! grep "fetch_count" replay-trace
+'
+
+test_expect_success 'backfill enables fetch-free merge' '
+	# Create a repo with two branches to merge.
+	git init merge-src &&
+	(
+		cd merge-src &&
+		git config uploadpack.allowfilter 1 &&
+		git config uploadpack.allowanysha1inwant 1 &&
+		test_commit merge-base &&
+		git checkout -b side &&
+		test_commit side-change &&
+		git checkout main &&
+		test_commit main-side-change
+	) &&
+	git clone --filter=blob:none \
+		"file://$(pwd)/merge-src" merge-dest &&
+
+	# The clone checked out main, fetching its blobs.
+	# Backfill the three endpoint commits needed for merge.
+	main_oid=$(git -C merge-dest rev-parse origin/main) &&
+	side_oid=$(git -C merge-dest rev-parse origin/side) &&
+	mbase=$(git -C merge-dest merge-base origin/main origin/side) &&
+	git -C merge-dest backfill --no-include-edges \
+		"$main_oid^!" "$side_oid^!" "$mbase^!" &&
+
+	# Merge should complete without promisor fetches.
+	GIT_TRACE2_EVENT="$(pwd)/merge-trace" git -C merge-dest \
+		merge origin/side -m "test merge" &&
+
+	! grep "fetch_count" merge-trace
+'
+
 . "$TEST_DIRECTORY"/lib-httpd.sh
 start_httpd
 
diff --git a/t/t5702-protocol-v2.sh b/t/t5702-protocol-v2.sh
index f826ac4..9f6cf41 100755
--- a/t/t5702-protocol-v2.sh
+++ b/t/t5702-protocol-v2.sh
@@ -869,14 +869,14 @@
 	test_commit -C client three
 }
 
-test_expect_success 'usage: --negotiate-only without --negotiation-tip' '
+test_expect_success 'usage: --negotiate-only without --negotiation-restrict' '
 	SERVER="server" &&
 	URI="file://$(pwd)/server" &&
 
 	setup_negotiate_only "$SERVER" "$URI" &&
 
 	cat >err.expect <<-\EOF &&
-	fatal: --negotiate-only needs one or more --negotiation-tip=*
+	fatal: --negotiate-only needs one or more --negotiation-restrict=*
 	EOF
 
 	test_must_fail git -c protocol.version=2 -C client fetch \
diff --git a/t/t5710-promisor-remote-capability.sh b/t/t5710-promisor-remote-capability.sh
index 357822c..b404ad9 100755
--- a/t/t5710-promisor-remote-capability.sh
+++ b/t/t5710-promisor-remote-capability.sh
@@ -76,6 +76,31 @@
 	cp "$path" "$path2"
 }
 
+# On Windows, `pwd` returns a path like 'D:/foo/bar'. Prepend '/' to turn
+# it into '/D:/foo/bar', which is what git expects in file:// URLs on Windows.
+# On Unix, the path already starts with '/', so this is a no-op.
+pwd_path=$(pwd)
+case "$pwd_path" in
+[a-zA-Z]:*) pwd_path="/$pwd_path" ;;
+esac
+
+# Allowed characters: alphanumeric, standard path/URI (_ . ~ / : -),
+# and those percent-encoded below (% space = , ;)
+rest=$(printf "%s" "$pwd_path" | tr -d 'a-zA-Z0-9_.~/:% =,;-')
+if test -n "$rest"
+then
+	skip_all="PWD contains unsupported special characters"
+	test_done
+fi
+
+TRASH_DIRECTORY_URL="file://$pwd_path"
+
+encoded_path=$(printf "%s" "$pwd_path" |
+	       sed -e 's/%/%25/g' -e 's/ /%20/g' -e 's/=/%3D/g' \
+		   -e 's/;/%3B/g' -e 's/,/%2C/g')
+
+ENCODED_TRASH_DIRECTORY_URL="file://$encoded_path"
+
 test_expect_success "setup for testing promisor remote advertisement" '
 	# Create another bare repo called "lop" (for Large Object Promisor)
 	git init --bare lop &&
@@ -88,7 +113,7 @@
 	initialize_server 1 "$oid" &&
 
 	# Configure lop as promisor remote for server
-	git -C server remote add lop "file://$(pwd)/lop" &&
+	git -C server remote add lop "$TRASH_DIRECTORY_URL/lop" &&
 	git -C server config remote.lop.promisor true &&
 
 	git -C lop config uploadpack.allowFilter true &&
@@ -104,7 +129,7 @@
 	# Clone from server to create a client
 	GIT_NO_LAZY_FETCH=0 git clone -c remote.lop.promisor=true \
 		-c remote.lop.fetch="+refs/heads/*:refs/remotes/lop/*" \
-		-c remote.lop.url="file://$(pwd)/lop" \
+		-c remote.lop.url="$TRASH_DIRECTORY_URL/lop" \
 		-c promisor.acceptfromserver=All \
 		--no-local --filter="blob:limit=5k" server client &&
 
@@ -119,7 +144,7 @@
 	# Clone from server to create a client
 	GIT_NO_LAZY_FETCH=0 git clone -c remote.lop.promisor=true \
 		-c remote.lop.fetch="+refs/heads/*:refs/remotes/lop/*" \
-		-c remote.lop.url="file://$(pwd)/lop" \
+		-c remote.lop.url="$TRASH_DIRECTORY_URL/lop" \
 		-c promisor.acceptfromserver=All \
 		--no-local --filter="blob:limit=5k" server client &&
 
@@ -137,7 +162,7 @@
 	# Clone from server to create a client
 	GIT_NO_LAZY_FETCH=0 git clone -c remote.lop.promisor=true \
 		-c remote.lop.fetch="+refs/heads/*:refs/remotes/lop/*" \
-		-c remote.lop.url="file://$(pwd)/lop" \
+		-c remote.lop.url="$TRASH_DIRECTORY_URL/lop" \
 		-c promisor.acceptfromserver=None \
 		--no-local --filter="blob:limit=5k" server client &&
 
@@ -156,8 +181,8 @@
 	git -C client init &&
 	git -C client config remote.lop.promisor true &&
 	git -C client config remote.lop.fetch "+refs/heads/*:refs/remotes/lop/*" &&
-	git -C client config remote.lop.url "file://$(pwd)/lop" &&
-	git -C client config remote.server.url "file://$(pwd)/server" &&
+	git -C client config remote.lop.url "$TRASH_DIRECTORY_URL/lop" &&
+	git -C client config remote.server.url "$TRASH_DIRECTORY_URL/server" &&
 	git -C client config remote.server.fetch "+refs/heads/*:refs/remotes/server/*" &&
 	git -C client config promisor.acceptfromserver All &&
 	GIT_NO_LAZY_FETCH=0 git -C client fetch --filter="blob:limit=5k" server &&
@@ -166,6 +191,75 @@
 	check_missing_objects server 1 "$oid"
 '
 
+test_expect_success "clone with two promisors but only one advertised" '
+	git -C server config promisor.advertise true &&
+	test_when_finished "rm -rf client unused_lop" &&
+
+	# Create a promisor that will be configured but not be used
+	git init --bare unused_lop &&
+
+	# Clone from server to create a client
+	GIT_TRACE="$(pwd)/trace" GIT_NO_LAZY_FETCH=0 git clone \
+		-c remote.unused_lop.promisor=true \
+		-c remote.unused_lop.fetch="+refs/heads/*:refs/remotes/unused_lop/*" \
+		-c remote.unused_lop.url="$TRASH_DIRECTORY_URL/unused_lop" \
+		-c remote.lop.promisor=true \
+		-c remote.lop.fetch="+refs/heads/*:refs/remotes/lop/*" \
+		-c remote.lop.url="$TRASH_DIRECTORY_URL/lop" \
+		-c promisor.acceptfromserver=All \
+		--no-local --filter="blob:limit=5k" server client &&
+
+	# Check that "unused_lop" appears before "lop" in the config
+	printf "remote.%s.promisor true\n" "unused_lop" "lop" "origin" >expect &&
+	git -C client config get --all --show-names --regexp "^remote\..*\.promisor$" >actual &&
+	test_cmp expect actual &&
+
+	# Check that "lop" was tried
+	test_grep " fetch lop " trace &&
+	# Check that "unused_lop" was not contacted
+	# This means "lop", the accepted promisor, was tried first
+	test_grep ! " fetch unused_lop " trace &&
+
+	# Check that the largest object is still missing on the server
+	check_missing_objects server 1 "$oid"
+'
+
+test_expect_success "init + fetch two promisors but only one advertised" '
+	git -C server config promisor.advertise true &&
+	test_when_finished "rm -rf client unused_lop" &&
+
+	# Create a promisor that will be configured but not be used
+	git init --bare unused_lop &&
+
+	mkdir client &&
+	git -C client init &&
+	git -C client config remote.unused_lop.promisor true &&
+	git -C client config remote.unused_lop.fetch "+refs/heads/*:refs/remotes/unused_lop/*" &&
+	git -C client config remote.unused_lop.url "$TRASH_DIRECTORY_URL/unused_lop" &&
+	git -C client config remote.lop.promisor true &&
+	git -C client config remote.lop.fetch "+refs/heads/*:refs/remotes/lop/*" &&
+	git -C client config remote.lop.url "$TRASH_DIRECTORY_URL/lop" &&
+	git -C client config remote.server.url "$TRASH_DIRECTORY_URL/server" &&
+	git -C client config remote.server.fetch "+refs/heads/*:refs/remotes/server/*" &&
+	git -C client config promisor.acceptfromserver All &&
+
+	# Check that "unused_lop" appears before "lop" in the config
+	printf "remote.%s.promisor true\n" "unused_lop" "lop" >expect &&
+	git -C client config get --all --show-names --regexp "^remote\..*\.promisor$" >actual &&
+	test_cmp expect actual &&
+
+	GIT_TRACE="$(pwd)/trace" GIT_NO_LAZY_FETCH=0 git -C client fetch --filter="blob:limit=5k" server &&
+
+	# Check that "lop" was tried
+	test_grep " fetch lop " trace &&
+	# Check that "unused_lop" was not contacted
+	# This means "lop", the accepted promisor, was tried first
+	test_grep ! " fetch unused_lop " trace &&
+
+	# Check that the largest object is still missing on the server
+	check_missing_objects server 1 "$oid"
+'
+
 test_expect_success "clone with promisor.acceptfromserver set to 'KnownName'" '
 	git -C server config promisor.advertise true &&
 	test_when_finished "rm -rf client" &&
@@ -173,7 +267,7 @@
 	# Clone from server to create a client
 	GIT_NO_LAZY_FETCH=0 git clone -c remote.lop.promisor=true \
 		-c remote.lop.fetch="+refs/heads/*:refs/remotes/lop/*" \
-		-c remote.lop.url="file://$(pwd)/lop" \
+		-c remote.lop.url="$TRASH_DIRECTORY_URL/lop" \
 		-c promisor.acceptfromserver=KnownName \
 		--no-local --filter="blob:limit=5k" server client &&
 
@@ -188,7 +282,7 @@
 	# Clone from server to create a client
 	GIT_NO_LAZY_FETCH=0 git clone -c remote.serverTwo.promisor=true \
 		-c remote.serverTwo.fetch="+refs/heads/*:refs/remotes/lop/*" \
-		-c remote.serverTwo.url="file://$(pwd)/lop" \
+		-c remote.serverTwo.url="$TRASH_DIRECTORY_URL/lop" \
 		-c promisor.acceptfromserver=KnownName \
 		--no-local --filter="blob:limit=5k" server client &&
 
@@ -225,7 +319,7 @@
 	# Clone from server to create a client
 	GIT_NO_LAZY_FETCH=0 git clone -c remote.lop.promisor=true \
 		-c remote.lop.fetch="+refs/heads/*:refs/remotes/lop/*" \
-		-c remote.lop.url="file://$(pwd)/lop" \
+		-c remote.lop.url="$TRASH_DIRECTORY_URL/lop" \
 		-c promisor.acceptfromserver=KnownUrl \
 		--no-local --filter="blob:limit=5k" server client &&
 
@@ -242,7 +336,7 @@
 	# Clone from server to create a client
 	GIT_NO_LAZY_FETCH=0 git clone -c remote.lop.promisor=true \
 		-c remote.lop.fetch="+refs/heads/*:refs/remotes/lop/*" \
-		-c remote.lop.url="file://$(pwd)/serverTwo" \
+		-c remote.lop.url="$TRASH_DIRECTORY_URL/serverTwo" \
 		-c promisor.acceptfromserver=KnownUrl \
 		--no-local --filter="blob:limit=5k" server client &&
 
@@ -257,7 +351,7 @@
 	git -C server config promisor.advertise true &&
 	test_when_finished "rm -rf client" &&
 
-	test_when_finished "git -C server config set remote.lop.url \"file://$(pwd)/lop\"" &&
+	test_when_finished "git -C server config set remote.lop.url \"$TRASH_DIRECTORY_URL/lop\"" &&
 	git -C server config unset remote.lop.url &&
 
 	# Clone from server to create a client
@@ -266,7 +360,7 @@
 	# missing, so the remote name will be used instead which will fail.
 	test_must_fail env GIT_NO_LAZY_FETCH=0 git clone -c remote.lop.promisor=true \
 		-c remote.lop.fetch="+refs/heads/*:refs/remotes/lop/*" \
-		-c remote.lop.url="file://$(pwd)/lop" \
+		-c remote.lop.url="$TRASH_DIRECTORY_URL/lop" \
 		-c promisor.acceptfromserver=KnownUrl \
 		--no-local --filter="blob:limit=5k" server client &&
 
@@ -278,7 +372,7 @@
 	git -C server config promisor.advertise true &&
 	test_when_finished "rm -rf client" &&
 
-	test_when_finished "git -C server config set remote.lop.url \"file://$(pwd)/lop\"" &&
+	test_when_finished "git -C server config set remote.lop.url \"$TRASH_DIRECTORY_URL/lop\"" &&
 	git -C server config set remote.lop.url "" &&
 
 	# Clone from server to create a client
@@ -287,7 +381,7 @@
 	# so the remote name will be used instead which will fail.
 	test_must_fail env GIT_NO_LAZY_FETCH=0 git clone -c remote.lop.promisor=true \
 		-c remote.lop.fetch="+refs/heads/*:refs/remotes/lop/*" \
-		-c remote.lop.url="file://$(pwd)/lop" \
+		-c remote.lop.url="$TRASH_DIRECTORY_URL/lop" \
 		-c promisor.acceptfromserver=KnownUrl \
 		--no-local --filter="blob:limit=5k" server client &&
 
@@ -311,13 +405,12 @@
 	GIT_TRACE_PACKET="$(pwd)/trace" GIT_NO_LAZY_FETCH=0 git clone \
 		-c remote.lop.promisor=true \
 		-c remote.lop.fetch="+refs/heads/*:refs/remotes/lop/*" \
-		-c remote.lop.url="file://$(pwd)/lop" \
+		-c remote.lop.url="$TRASH_DIRECTORY_URL/lop" \
 		-c promisor.acceptfromserver=All \
 		--no-local --filter="blob:limit=5k" server client &&
 
 	# Check that fields are properly transmitted
-	ENCODED_URL=$(echo "file://$(pwd)/lop" | sed -e "s/ /%20/g") &&
-	PR1="name=lop,url=$ENCODED_URL,partialCloneFilter=blob:none" &&
+	PR1="name=lop,url=$ENCODED_TRASH_DIRECTORY_URL/lop,partialCloneFilter=blob:none" &&
 	PR2="name=otherLop,url=https://invalid.invalid,partialCloneFilter=blob:limit=10k,token=fooBar" &&
 	test_grep "clone< promisor-remote=$PR1;$PR2" trace &&
 	test_grep "clone> promisor-remote=lop;otherLop" trace &&
@@ -342,15 +435,14 @@
 	GIT_TRACE_PACKET="$(pwd)/trace" GIT_NO_LAZY_FETCH=0 git clone \
 		-c remote.lop.promisor=true \
 		-c remote.lop.fetch="+refs/heads/*:refs/remotes/lop/*" \
-		-c remote.lop.url="file://$(pwd)/lop" \
+		-c remote.lop.url="$TRASH_DIRECTORY_URL/lop" \
 		-c remote.lop.partialCloneFilter="blob:none" \
 		-c promisor.acceptfromserver=All \
 		-c promisor.checkFields=partialcloneFilter \
 		--no-local --filter="blob:limit=5k" server client &&
 
 	# Check that fields are properly transmitted
-	ENCODED_URL=$(echo "file://$(pwd)/lop" | sed -e "s/ /%20/g") &&
-	PR1="name=lop,url=$ENCODED_URL,partialCloneFilter=blob:none" &&
+	PR1="name=lop,url=$ENCODED_TRASH_DIRECTORY_URL/lop,partialCloneFilter=blob:none" &&
 	PR2="name=otherLop,url=https://invalid.invalid,partialCloneFilter=blob:limit=10k,token=fooBar" &&
 	test_grep "clone< promisor-remote=$PR1;$PR2" trace &&
 	test_grep "clone> promisor-remote=lop" trace &&
@@ -380,7 +472,7 @@
 	GIT_TRACE_PACKET="$(pwd)/trace" GIT_NO_LAZY_FETCH=0 git clone \
 		-c remote.lop.promisor=true \
 		-c remote.lop.fetch="+refs/heads/*:refs/remotes/lop/*" \
-		-c remote.lop.url="file://$(pwd)/lop" \
+		-c remote.lop.url="$TRASH_DIRECTORY_URL/lop" \
 		-c remote.lop.token="fooYYY" \
 		-c remote.lop.partialCloneFilter="blob:none" \
 		-c promisor.acceptfromserver=All \
@@ -432,7 +524,7 @@
 
 	GIT_TRACE_PACKET="$(pwd)/trace" GIT_NO_LAZY_FETCH=0 git clone \
 		-c remote.lop.promisor=true \
-		-c remote.lop.url="file://$(pwd)/lop" \
+		-c remote.lop.url="$TRASH_DIRECTORY_URL/lop" \
 		-c promisor.acceptfromserver=All \
 		--no-local --filter=auto server client 2>err &&
 
@@ -489,7 +581,7 @@
 	# Clone from server to create a client
 	GIT_NO_LAZY_FETCH=0 git clone -c remote.lop.promisor=true \
 		-c remote.lop.fetch="+refs/heads/*:refs/remotes/lop/*" \
-		-c remote.lop.url="file://$(pwd)/lop" \
+		-c remote.lop.url="$TRASH_DIRECTORY_URL/lop" \
 		-c promisor.acceptfromserver=All \
 		--no-local --filter="blob:limit=5k" server client &&
 
diff --git a/t/t6000-rev-list-misc.sh b/t/t6000-rev-list-misc.sh
index d0a2a86..a95ba57 100755
--- a/t/t6000-rev-list-misc.sh
+++ b/t/t6000-rev-list-misc.sh
@@ -263,4 +263,35 @@
 	test_grep "cannot be used together" err
 '
 
+test_expect_success 'rev-list --maximal-only and --pretty' '
+	test_when_finished rm -rf repo &&
+
+	git init repo &&
+	test_commit -C repo 1 &&
+	oid1=$(git -C repo rev-parse HEAD) &&
+	test_commit -C repo 2 &&
+	oid2=$(git -C repo rev-parse HEAD) &&
+	git -C repo checkout --detach HEAD~1 &&
+	test_commit -C repo 3 &&
+	oid3=$(git -C repo rev-parse HEAD) &&
+
+	cat >expect <<-EOF &&
+	commit $oid3
+	$oid3
+	commit $oid2
+	$oid2
+	EOF
+
+	git -C repo rev-list --pretty="%H" --maximal-only $oid1 $oid2 $oid3 >out &&
+	test_cmp expect out &&
+
+	cat >expect <<-EOF &&
+	$oid3
+	$oid2
+	EOF
+
+	git -C repo log --pretty="%H" --maximal-only $oid1 $oid2 $oid3 >out &&
+	test_cmp expect out
+'
+
 test_done
diff --git a/t/t6002-rev-list-bisect.sh b/t/t6002-rev-list-bisect.sh
index daa009c..f2de40b 100755
--- a/t/t6002-rev-list-bisect.sh
+++ b/t/t6002-rev-list-bisect.sh
@@ -27,13 +27,16 @@
 	# Test if bisection size is close to half of list size within
 	# tolerance.
 	#
-	_bisect_err=$(expr $_list_size - $_bisection_size \* 2)
-	test "$_bisect_err" -lt 0 && _bisect_err=$(expr 0 - $_bisect_err)
-	_bisect_err=$(expr $_bisect_err / 2) ; # floor
+	_bisect_err=$(($_list_size - $_bisection_size * 2))
+	if test "$_bisect_err" -lt 0
+	then
+		_bisect_err=$((0 - $_bisect_err))
+	fi
+	_bisect_err=$(($_bisect_err / 2)) ; # floor
 
-	test_expect_success \
-	"bisection diff $_bisect_option $_head $* <= $_max_diff" \
-	'test $_bisect_err -le $_max_diff'
+	test_expect_success "bisection diff $_bisect_option $_head $* <= $_max_diff" '
+		test $_bisect_err -le $_max_diff
+	'
 }
 
 date >path0
diff --git a/t/t6030-bisect-porcelain.sh b/t/t6030-bisect-porcelain.sh
index 1ba9ca2..0811162 100755
--- a/t/t6030-bisect-porcelain.sh
+++ b/t/t6030-bisect-porcelain.sh
@@ -258,7 +258,7 @@
 	git bisect start $HASH4 $HASH1 &&
 	git bisect skip &&
 	git bisect bad > my_bisect_log.txt &&
-	grep "$HASH2 is the first bad commit" my_bisect_log.txt
+	grep "$HASH2 is the first '\''bad'\'' commit" my_bisect_log.txt
 '
 
 # $HASH1 is good, $HASH4 is bad, we skip $HASH3 and $HASH2
@@ -269,7 +269,7 @@
 	git bisect start $HASH4 $HASH1 &&
 	git bisect skip &&
 	test_expect_code 2 git bisect skip >my_bisect_log.txt &&
-	grep "first bad commit could be any of" my_bisect_log.txt &&
+	grep "first '\''bad'\'' commit could be any of" my_bisect_log.txt &&
 	! grep $HASH1 my_bisect_log.txt &&
 	grep $HASH2 my_bisect_log.txt &&
 	grep $HASH3 my_bisect_log.txt &&
@@ -285,7 +285,7 @@
 	git bisect start $HASH4 $HASH1 &&
 	git bisect skip &&
 	test_expect_code 2 git bisect good >my_bisect_log.txt &&
-	grep "first bad commit could be any of" my_bisect_log.txt &&
+	grep "first '\''bad'\'' commit could be any of" my_bisect_log.txt &&
 	! grep $HASH1 my_bisect_log.txt &&
 	! grep $HASH2 my_bisect_log.txt &&
 	grep $HASH3 my_bisect_log.txt &&
@@ -304,7 +304,7 @@
 	git bisect good $HASH1 &&
 	git bisect skip &&
 	test_expect_code 2 git bisect good >my_bisect_log.txt &&
-	grep "first bad commit could be any of" my_bisect_log.txt &&
+	grep "first '\''bad'\'' commit could be any of" my_bisect_log.txt &&
 	! grep $HASH1 my_bisect_log.txt &&
 	! grep $HASH2 my_bisect_log.txt &&
 	grep $HASH3 my_bisect_log.txt &&
@@ -348,8 +348,8 @@
 	test_bisect_run_args <<-'EOF_ARGS' 6<<-EOF_OUT 7<<-'EOF_ERR'
 	EOF_ARGS
 	running './run.sh'
-	$HASH4 is the first bad commit
-	bisect found first bad commit
+	$HASH4 is the first 'bad' commit
+	bisect found first 'bad' commit
 	EOF_OUT
 	EOF_ERR
 "
@@ -359,8 +359,8 @@
 	<-->
 	EOF_ARGS
 	running './run.sh' '--'
-	$HASH4 is the first bad commit
-	bisect found first bad commit
+	$HASH4 is the first 'bad' commit
+	bisect found first 'bad' commit
 	EOF_OUT
 	EOF_ERR
 "
@@ -373,8 +373,8 @@
 	<bar>
 	EOF_ARGS
 	running './run.sh' '--log' 'foo' '--no-log' 'bar'
-	$HASH4 is the first bad commit
-	bisect found first bad commit
+	$HASH4 is the first 'bad' commit
+	bisect found first 'bad' commit
 	EOF_OUT
 	EOF_ERR
 "
@@ -384,8 +384,8 @@
 	<--bisect-start>
 	EOF_ARGS
 	running './run.sh' '--bisect-start'
-	$HASH4 is the first bad commit
-	bisect found first bad commit
+	$HASH4 is the first 'bad' commit
+	bisect found first 'bad' commit
 	EOF_OUT
 	EOF_ERR
 "
@@ -418,7 +418,7 @@
 	fi
 	EOF
 	cat <<-'EOF' >expect &&
-	unable to verify './fail.sh' on good revision
+	unable to verify './fail.sh' on 'good' revision
 	EOF
 	test_when_finished 'git bisect reset' &&
 	git bisect start &&
@@ -439,7 +439,7 @@
 	git bisect good $HASH1 &&
 	git bisect bad $HASH4 &&
 	git bisect run ./test_script.sh >my_bisect_log.txt &&
-	grep "$HASH3 is the first bad commit" my_bisect_log.txt &&
+	grep "$HASH3 is the first '\''bad'\'' commit" my_bisect_log.txt &&
 	git bisect reset
 '
 
@@ -461,7 +461,7 @@
 	EOF
 	git bisect start $HASH4 $HASH1 &&
 	git bisect run ./test_script.sh >my_bisect_log.txt &&
-	grep "$HASH4 is the first bad commit" my_bisect_log.txt &&
+	grep "$HASH4 is the first '\''bad'\'' commit" my_bisect_log.txt &&
 	git bisect reset
 '
 
@@ -474,7 +474,7 @@
 	git bisect good $HASH1 &&
 	git bisect bad $HASH4 &&
 	git bisect run ./test_script.sh >my_bisect_log.txt &&
-	grep "$HASH3 is the first bad commit" my_bisect_log.txt
+	grep "$HASH3 is the first '\''bad'\'' commit" my_bisect_log.txt
 '
 
 test_expect_success POSIXPERM 'bisect run fails with non-executable test script' '
@@ -485,7 +485,7 @@
 	git bisect good $HASH1 &&
 	git bisect bad $HASH4 &&
 	test_must_fail git bisect run ./not-executable.sh >my_bisect_log.txt &&
-	! grep "is the first bad commit" my_bisect_log.txt
+	! grep "is the first '\''bad'\'' commit" my_bisect_log.txt
 '
 
 test_expect_success 'bisect run accepts exit code 127 as bad' '
@@ -497,7 +497,7 @@
 	git bisect good $HASH1 &&
 	git bisect bad $HASH4 &&
 	git bisect run ./test_script.sh >my_bisect_log.txt &&
-	grep "$HASH3 is the first bad commit" my_bisect_log.txt
+	grep "$HASH3 is the first '\''bad'\'' commit" my_bisect_log.txt
 '
 
 test_expect_success 'bisect run fails with missing test script' '
@@ -507,7 +507,7 @@
 	git bisect good $HASH1 &&
 	git bisect bad $HASH4 &&
 	test_must_fail git bisect run ./does-not-exist.sh >my_bisect_log.txt &&
-	! grep "is the first bad commit" my_bisect_log.txt
+	! grep "is the first '\''bad'\'' commit" my_bisect_log.txt
 '
 
 # $HASH1 is good, $HASH5 is bad, we skip $HASH3
@@ -520,14 +520,14 @@
 	git bisect start $HASH5 $HASH1 &&
 	git bisect skip &&
 	git bisect good > my_bisect_log.txt &&
-	grep "$HASH5 is the first bad commit" my_bisect_log.txt &&
+	grep "$HASH5 is the first '\''bad'\'' commit" my_bisect_log.txt &&
 	git bisect log > log_to_replay.txt &&
 	git bisect reset
 '
 
 test_expect_success 'bisect skip and bisect replay' '
 	git bisect replay log_to_replay.txt > my_bisect_log.txt &&
-	grep "$HASH5 is the first bad commit" my_bisect_log.txt &&
+	grep "$HASH5 is the first '\''bad'\'' commit" my_bisect_log.txt &&
 	git bisect reset
 '
 
@@ -541,7 +541,7 @@
 	EOF
 	git bisect start $HASH6 $HASH1 &&
 	test_expect_code 2 git bisect run ./test_script.sh >my_bisect_log.txt &&
-	grep "first bad commit could be any of" my_bisect_log.txt &&
+	grep "first '\''bad'\'' commit could be any of" my_bisect_log.txt &&
 	! grep $HASH3 my_bisect_log.txt &&
 	! grep $HASH6 my_bisect_log.txt &&
 	grep $HASH4 my_bisect_log.txt &&
@@ -560,7 +560,7 @@
 	EOF
 	git bisect start $HASH7 $HASH1 &&
 	git bisect run ./test_script.sh >my_bisect_log.txt &&
-	grep "$HASH6 is the first bad commit" my_bisect_log.txt
+	grep "$HASH6 is the first '\''bad'\'' commit" my_bisect_log.txt
 '
 
 test_expect_success 'bisect skip only one range' '
@@ -569,7 +569,7 @@
 	git bisect skip $HASH1..$HASH5 &&
 	test "$HASH6" = "$(git rev-parse --verify HEAD)" &&
 	test_must_fail git bisect bad > my_bisect_log.txt &&
-	grep "first bad commit could be any of" my_bisect_log.txt
+	grep "first '\''bad'\'' commit could be any of" my_bisect_log.txt
 '
 
 test_expect_success 'bisect skip many ranges' '
@@ -578,7 +578,7 @@
 	git bisect skip $HASH2 $HASH2.. ..$HASH5 &&
 	test "$HASH6" = "$(git rev-parse --verify HEAD)" &&
 	test_must_fail git bisect bad > my_bisect_log.txt &&
-	grep "first bad commit could be any of" my_bisect_log.txt
+	grep "first '\''bad'\'' commit could be any of" my_bisect_log.txt
 '
 
 test_expect_success 'bisect starting with a detached HEAD' '
@@ -594,7 +594,7 @@
 test_expect_success 'bisect errors out if bad and good are mistaken' '
 	git bisect reset &&
 	test_must_fail git bisect start $HASH2 $HASH4 2> rev_list_error &&
-	test_grep "mistook good and bad" rev_list_error &&
+	test_grep "mistook '\''good'\'' and '\''bad'\''" rev_list_error &&
 	git bisect reset
 '
 
@@ -610,7 +610,7 @@
 	rev_hash6=$(git rev-parse --verify HEAD) &&
 	test "$rev_hash6" = "$HASH6" &&
 	git bisect good > my_bisect_log.txt &&
-	grep "$HASH7 is the first bad commit" my_bisect_log.txt &&
+	grep "$HASH7 is the first '\''bad'\'' commit" my_bisect_log.txt &&
 	git bisect reset &&
 	rev_hash6=$(git rev-parse --verify bisect) &&
 	test "$rev_hash6" = "$HASH6" &&
@@ -703,7 +703,7 @@
 	git bisect good $HASH4 &&
 	git bisect bad $B_HASH &&
 	git bisect run ./test_script.sh >my_bisect_log.txt &&
-	grep "$B_HASH is the first bad commit" my_bisect_log.txt &&
+	grep "$B_HASH is the first '\''bad'\'' commit" my_bisect_log.txt &&
 	git bisect reset &&
 	test_path_is_missing .git/BISECT_FIRST_PARENT
 '
@@ -777,7 +777,7 @@
 	para1=$(git rev-parse --verify HEAD) &&
 	test "$para1" = "$PARA_HASH1" &&
 	git bisect bad > my_bisect_log.txt &&
-	grep "$PARA_HASH1 is the first bad commit" my_bisect_log.txt
+	grep "$PARA_HASH1 is the first '\''bad'\'' commit" my_bisect_log.txt
 '
 
 test_expect_success 'restricting bisection on one dir and a file' '
@@ -795,7 +795,7 @@
 	para1=$(git rev-parse --verify HEAD) &&
 	test "$para1" = "$PARA_HASH1" &&
 	git bisect good > my_bisect_log.txt &&
-	grep "$PARA_HASH4 is the first bad commit" my_bisect_log.txt
+	grep "$PARA_HASH4 is the first '\''bad'\'' commit" my_bisect_log.txt
 '
 
 test_expect_success 'skipping away from skipped commit' '
@@ -826,7 +826,7 @@
 			"test \$(git rev-list BISECT_HEAD ^$HASH2 --max-count=1 | wc -l) = 0" \
 			>../nocheckout.log
 	) &&
-	grep "$HASH3 is the first bad commit" nocheckout.log
+	grep "$HASH3 is the first '\''bad'\'' commit" nocheckout.log
 '
 
 
@@ -841,7 +841,7 @@
 			"test \$(git rev-list BISECT_HEAD ^$HASH2 --max-count=1 | wc -l) = 0" \
 			>../defaulted.log
 	) &&
-	grep "$HASH3 is the first bad commit" defaulted.log
+	grep "$HASH3 is the first '\''bad'\'' commit" defaulted.log
 '
 
 #
@@ -969,7 +969,7 @@
 git bisect start '$HASH4' '$HASH2'
 # good: [$HASH3] Add <3: Another new day for git> into <hello>.
 git bisect good $HASH3
-# first bad commit: [$HASH4] Add <4: Ciao for now> into <hello>.
+# first 'bad' commit: [$HASH4] Add <4: Ciao for now> into <hello>.
 EOF
 
 test_expect_success 'bisect log: successful result' '
@@ -988,8 +988,8 @@
 # skip: [$HASH3] Add <3: Another new day for git> into <hello>.
 git bisect skip $HASH3
 # only skipped commits left to test
-# possible first bad commit: [$HASH4] Add <4: Ciao for now> into <hello>.
-# possible first bad commit: [$HASH3] Add <3: Another new day for git> into <hello>.
+# possible first 'bad' commit: [$HASH4] Add <4: Ciao for now> into <hello>.
+# possible first 'bad' commit: [$HASH3] Add <3: Another new day for git> into <hello>.
 EOF
 
 test_expect_success 'bisect log: only skip commits left' '
@@ -1031,21 +1031,21 @@
 	git bisect new $HASH4 &&
 	git bisect new &&
 	git bisect new >bisect_result &&
-	grep "$HASH2 is the first new commit" bisect_result &&
+	grep "$HASH2 is the first '\''new'\'' commit" bisect_result &&
 	git bisect log >log_to_replay.txt &&
 	git bisect reset
 '
 
 test_expect_success 'bisect replay with old and new' '
 	git bisect replay log_to_replay.txt >bisect_result &&
-	grep "$HASH2 is the first new commit" bisect_result &&
+	grep "$HASH2 is the first '\''new'\'' commit" bisect_result &&
 	git bisect reset
 '
 
 test_expect_success 'bisect replay with CRLF log' '
 	append_cr <log_to_replay.txt >log_to_replay_crlf.txt &&
 	git bisect replay log_to_replay_crlf.txt >bisect_result_crlf &&
-	grep "$HASH2 is the first new commit" bisect_result_crlf &&
+	grep "$HASH2 is the first '\''new'\'' commit" bisect_result_crlf &&
 	git bisect reset
 '
 
@@ -1077,12 +1077,14 @@
 
 test_expect_success 'bisect start with one term1 and term2' '
 	git bisect reset &&
-	git bisect start --term-old term2 --term-new term1 &&
-	git bisect term2 $HASH1 &&
+	git bisect start --term-old term2 --term-new term1 >bisect_result &&
+	test_grep "status: waiting for both '\''term2'\'' and '\''term1'\'' commits" bisect_result &&
+	git bisect term2 $HASH1 >bisect_result &&
+	test_grep "status: waiting for '\''term1'\'' commit, 1 '\''term2'\'' commit known" bisect_result &&
 	git bisect term1 $HASH4 &&
 	git bisect term1 &&
 	git bisect term1 >bisect_result &&
-	grep "$HASH2 is the first term1 commit" bisect_result &&
+	test_grep "$HASH2 is the first '\''term1'\'' commit" bisect_result &&
 	git bisect log >log_to_replay.txt &&
 	git bisect reset
 '
@@ -1099,7 +1101,17 @@
 
 test_expect_success 'bisect replay with term1 and term2' '
 	git bisect replay log_to_replay.txt >bisect_result &&
-	grep "$HASH2 is the first term1 commit" bisect_result &&
+	grep "$HASH2 is the first '\''term1'\'' commit" bisect_result &&
+	git bisect reset
+'
+
+test_expect_success 'bisect run term1 term2' '
+	git bisect reset &&
+	git bisect start --term-new term1 --term-old term2 $HASH4 $HASH1 &&
+	git bisect term1 &&
+	git bisect run false >bisect_result &&
+	test_grep "bisect found first '\''term1'\'' commit" bisect_result &&
+	git bisect log >log_to_replay.txt &&
 	git bisect reset
 '
 
@@ -1108,7 +1120,7 @@
 	git bisect start --term-new term1 --term-old term2 $HASH4 $HASH1 &&
 	git bisect term1 &&
 	git bisect term1 >bisect_result &&
-	grep "$HASH2 is the first term1 commit" bisect_result &&
+	grep "$HASH2 is the first '\''term1'\'' commit" bisect_result &&
 	git bisect log >log_to_replay.txt &&
 	git bisect reset
 '
@@ -1142,8 +1154,8 @@
 	git bisect start --term-bad=one --term-good=two &&
 	git bisect terms >actual &&
 	cat <<-EOF >expected &&
-	Your current terms are two for the old state
-	and one for the new state.
+	Your current terms are '\''two'\'' for the old state
+	and '\''one'\'' for the new state.
 	EOF
 	test_cmp expected actual &&
 	git bisect terms --term-bad >actual &&
@@ -1200,7 +1212,7 @@
 	git bisect good tag-one &&
 	git bisect bad tag-two >output &&
 	bad=$(git rev-parse --verify tag-two^{commit}) &&
-	grep "$bad is the first bad commit" output
+	grep "$bad is the first '\''bad'\'' commit" output
 '
 
 test_expect_success 'bisect run fails with exit code equals or greater than 128' '
@@ -1224,29 +1236,29 @@
 test_expect_success 'bisect state output with multiple good commits' '
 	git bisect reset &&
 	git bisect start >output &&
-	grep "waiting for both good and bad commits" output &&
+	grep "waiting for both '\''good'\'' and '\''bad'\'' commits" output &&
 	git bisect log >output &&
-	grep "waiting for both good and bad commits" output &&
+	grep "waiting for both '\''good'\'' and '\''bad'\'' commits" output &&
 	git bisect good "$HASH1" >output &&
-	grep "waiting for bad commit, 1 good commit known" output &&
+	grep "waiting for '\''bad'\'' commit, 1 '\''good'\'' commit known" output &&
 	git bisect log >output &&
-	grep "waiting for bad commit, 1 good commit known" output &&
+	grep "waiting for '\''bad'\'' commit, 1 '\''good'\'' commit known" output &&
 	git bisect good "$HASH2" >output &&
-	grep "waiting for bad commit, 2 good commits known" output &&
+	grep "waiting for '\''bad'\'' commit, 2 '\''good'\'' commits known" output &&
 	git bisect log >output &&
-	grep "waiting for bad commit, 2 good commits known" output
+	grep "waiting for '\''bad'\'' commit, 2 '\''good'\'' commits known" output
 '
 
 test_expect_success 'bisect state output with bad commit' '
 	git bisect reset &&
 	git bisect start >output &&
-	grep "waiting for both good and bad commits" output &&
+	grep "waiting for both '\''good'\'' and '\''bad'\'' commits" output &&
 	git bisect log >output &&
-	grep "waiting for both good and bad commits" output &&
+	grep "waiting for both '\''good'\'' and '\''bad'\'' commits" output &&
 	git bisect bad "$HASH4" >output &&
-	grep -F "waiting for good commit(s), bad commit known" output &&
+	grep -F "waiting for '\''good'\'' commit(s), '\''bad'\'' commit known" output &&
 	git bisect log >output &&
-	grep -F "waiting for good commit(s), bad commit known" output
+	grep -F "waiting for '\''good'\'' commit(s), '\''bad'\'' commit known" output
 '
 
 test_expect_success 'verify correct error message' '
diff --git a/t/t6101-rev-parse-parents.sh b/t/t6101-rev-parse-parents.sh
index 5f55ab9..7281889 100755
--- a/t/t6101-rev-parse-parents.sh
+++ b/t/t6101-rev-parse-parents.sh
@@ -39,7 +39,8 @@
 '
 
 test_expect_success 'start is valid' '
-	git rev-parse start | grep "^$OID_REGEX$"
+	git rev-parse start >actual &&
+	test_grep "^$OID_REGEX$" actual
 '
 
 test_expect_success 'start^0' '
diff --git a/t/t6112-rev-list-filters-objects.sh b/t/t6112-rev-list-filters-objects.sh
index 39211ef..e0a825b 100755
--- a/t/t6112-rev-list-filters-objects.sh
+++ b/t/t6112-rev-list-filters-objects.sh
@@ -623,9 +623,9 @@
 	omitted_2=$(echo a     | git hash-object --stdin) &&
 	omitted_3=$(echo abcde | git hash-object --stdin) &&
 
-	grep ~$omitted_1 actual &&
-	grep ~$omitted_2 actual &&
-	grep ~$omitted_3 actual &&
+	grep "~$omitted_1" actual &&
+	grep "~$omitted_2" actual &&
+	grep "~$omitted_3" actual &&
 	test_line_count = 3 actual
 '
 
diff --git a/t/t6120-describe.sh b/t/t6120-describe.sh
index 2c70cc5..8ee3d2c 100755
--- a/t/t6120-describe.sh
+++ b/t/t6120-describe.sh
@@ -298,6 +298,20 @@
 	test_cmp expect actual
 '
 
+test_expect_success 'name-rev --annotate-stdin --name-only' '
+	>expect.unsorted &&
+	for rev in $(git rev-list --all)
+	do
+		name=$(git name-rev --name-only $rev) &&
+		echo "$name" >>expect.unsorted || return 1
+	done &&
+	sort <expect.unsorted >expect &&
+	git name-rev --annotate-stdin --name-only \
+		<list >actual.unsorted &&
+	sort <actual.unsorted >actual &&
+	test_cmp expect actual
+'
+
 test_expect_success 'name-rev --stdin deprecated' '
 	git rev-list --all >list &&
 	if ! test_have_prereq WITH_BREAKING_CHANGES
@@ -787,4 +801,198 @@
 	test_must_fail git cat-file -t "refs/tags/super-invalid/./../...../ ~^:/?*[////\\\\\\&}/busted.lock-42-g"$(cat out)
 '
 
+test_expect_success 'setup: format-rev' '
+	mkdir repo-format &&
+	git -C repo-format init &&
+	test_commit -C repo-format first &&
+	test_commit -C repo-format second &&
+	test_commit -C repo-format third &&
+	test_commit -C repo-format fourth &&
+	test_commit -C repo-format fifth &&
+	test_commit -C repo-format sixth &&
+	test_commit -C repo-format seventh &&
+	test_commit -C repo-format eighth
+'
+
+test_expect_success 'format-rev --stdin-mode=revs' '
+	cat >expect <<-\EOF &&
+	eighth
+	seventh
+	fifth
+	EOF
+	git -C repo-format format-rev --stdin-mode=revs \
+		--format=%s >actual <<-\EOF &&
+	HEAD
+	HEAD~
+	HEAD~3
+	EOF
+	test_cmp expect actual
+'
+
+test_expect_success 'format-rev --stdin-mode=text from rev-list same as log' '
+	git -C repo-format log --format=reference >expect &&
+	test_file_not_empty expect &&
+	git -C repo-format rev-list HEAD >list &&
+	git -C repo-format format-rev --stdin-mode=text \
+		--format=reference <list >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'format-rev --stdin-mode=text with running text and tree oid' '
+	cmit_oid=$(git -C repo-format rev-parse fifth) &&
+	reference=$(git -C repo-format log -n1 --format=reference fifth) &&
+	tree=$(git -C repo-format rev-parse HEAD^{tree}) &&
+	cat >expect <<-EOF &&
+	We thought we fixed this in ${reference}.
+	But look at this tree: ${tree}.
+	EOF
+	git -C repo-format format-rev --stdin-mode=text --format=reference \
+		>actual <<-EOF &&
+	We thought we fixed this in ${cmit_oid}.
+	But look at this tree: ${tree}.
+	EOF
+	test_cmp expect actual
+'
+
+test_expect_success 'format-rev with %N (note)' '
+	test_when_finished "git -C repo-format notes remove" &&
+	git -C repo-format notes add -m"Make a note" &&
+	printf "Make a note\n\n\n" >expect &&
+	git -C repo-format format-rev --stdin-mode=revs \
+		--format="tformat:%N" \
+		>actual <<-\EOF &&
+	HEAD
+	HEAD~
+	EOF
+	test_cmp expect actual
+'
+
+test_expect_success 'format-rev --notes<ref> (custom notes ref)' '
+	# One custom notes ref
+	test_when_finished "git -C repo-format notes remove" &&
+	test_when_finished "git -C repo-format notes --ref=word remove" &&
+	git -C repo-format notes add -m"default" &&
+	git -C repo-format notes --ref=word add -m"custom" &&
+	printf "custom\n\n" >expect &&
+	git -C repo-format format-rev --stdin-mode=revs \
+		--format="tformat:%N" \
+		--notes=word \
+		>actual <<-\EOF &&
+	HEAD
+	EOF
+	test_cmp expect actual &&
+	# Glob all
+	printf "default\ncustom\n\n" >expect &&
+	git -C repo-format format-rev --stdin-mode=revs \
+		--format="tformat:%N" \
+		--notes=* >actual <<-\EOF &&
+	HEAD
+	EOF
+	test_cmp expect actual
+'
+
+test_expect_success 'format-rev --stdin-mode=revs on annotated tag peels to commit' '
+	test_when_finished "git -C repo-format tag -d version" &&
+	git -C repo-format tag -a -m"new version" version &&
+	cat >expect <<-\EOF &&
+	eighth
+	EOF
+	git -C repo-format format-rev --stdin-mode=revs \
+		--format=%s \
+		>actual <<-\EOF &&
+	version
+	EOF
+	test_cmp expect actual
+'
+
+test_expect_success 'format-rev --stdin-mode=revs lookup failures' '
+	test_when_finished "git -C repo-format tag -d tag-to-tree" &&
+	invalid_syntax=not-valid &&
+	non_existing_oid=${EMPTY_BLOB} &&
+	tree=$(git -C repo-format rev-parse eighth^{tree}) &&
+	git -C repo-format tag -a -mmessage tag-to-tree "$tree" &&
+	tag_to_tree=$(git -C repo-format rev-parse tag-to-tree) &&
+	cat >expect <<-EOF &&
+	Could not get object name for ${invalid_syntax}. Skipping.
+	Could not get object for ${non_existing_oid}. Skipping.
+	Could not get commit for ${tree}. Skipping.
+	Could not get commit for ${tag_to_tree}. Skipping.
+	EOF
+	git -C repo-format format-rev --stdin-mode=revs \
+		--format=%s \
+		2>actual >out <<-EOF &&
+	${invalid_syntax}
+	${non_existing_oid}
+	${tree}
+	${tag_to_tree}
+	EOF
+	test_line_count = 0 out &&
+	test_cmp expect actual
+'
+
+
+test_expect_success 'format-rev -z --stdin-mode=text with object name lookup failures' '
+	printf "%s\0" "$(git -C repo-format rev-parse HEAD)" >input &&
+	printf "%s\0" "$(git -C repo-format rev-parse HEAD^{tree})" >>input &&
+	printf "%s\0" "$EMPTY_BLOB" >>input &&
+	printf "%s\0" "$(git -C repo-format log --format=%s -1)" >expect &&
+	printf "%s\0" "$(git -C repo-format rev-parse HEAD^{tree})" >>expect &&
+	printf "%s\0" "$EMPTY_BLOB" >>expect &&
+	git -C repo-format format-rev --stdin-mode=text \
+		--format=%s -z <input >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'setup: format-rev input and output separators' '
+	git -C repo-format rev-list HEAD >input-lf &&
+	git -C repo-format rev-list -z HEAD >input-nul &&
+	git -C repo-format log --format=%s >output-lf &&
+	git -C repo-format log -z --format=%s >output-nul &&
+	echo revs >stdin-modes &&
+	echo text >>stdin-modes
+'
+
+while read mode
+do
+	test_expect_success "format-rev -z --stdin-mode=$mode" '
+		cat output-nul >expect &&
+		git -C repo-format format-rev --stdin-mode="$mode" \
+			--format=%s -z <input-nul >actual &&
+		test_cmp expect actual
+	'
+
+	test_expect_success "format-rev -z --no-null-input --no-null-output --stdin-mode=$mode" '
+		cat output-lf >expect &&
+		git -C repo-format format-rev --stdin-mode="$mode" \
+			--format=%s -z --no-null-input --no-null-output \
+			<input-lf >actual &&
+		test_cmp expect actual
+	'
+
+	test_expect_success "format-rev ---null-input --stdin-mode=$mode" '
+		cat output-lf >expect &&
+		git -C repo-format format-rev --stdin-mode="$mode" \
+			--format=%s --null-input \
+			<input-nul >actual &&
+		test_cmp expect actual
+	'
+
+	test_expect_success "format-rev --null-output --stdin-mode=$mode" '
+		cat output-nul >expect &&
+		git -C repo-format format-rev --stdin-mode="$mode" \
+			--format=%s --null-output \
+			<input-lf >actual &&
+		test_cmp expect actual
+	'
+
+	test_expect_success "format-rev -z --stdin-mode=$mode with multi-line output" '
+		format="%s%n%aI" &&
+		git -C repo-format log -z --format="$format" \
+			>expect &&
+		git -C repo-format format-rev --stdin-mode="$mode" \
+			--format="$format" -z <input-nul >actual &&
+		test_cmp expect actual
+	'
+done <stdin-modes
+
 test_done
diff --git a/t/t6408-merge-up-to-date.sh b/t/t6408-merge-up-to-date.sh
index 7763c1b..be0840e 100755
--- a/t/t6408-merge-up-to-date.sh
+++ b/t/t6408-merge-up-to-date.sh
@@ -89,4 +89,14 @@
 	test "$expect" = "$current"
 '
 
+test_expect_success 'merge octopus already up to date' '
+
+	git reset --hard c2 &&
+	test_tick &&
+	git merge c0 c1 &&
+	expect=$(git rev-parse c2) &&
+	current=$(git rev-parse HEAD) &&
+	test "$expect" = "$current"
+'
+
 test_done
diff --git a/t/t6429-merge-sequence-rename-caching.sh b/t/t6429-merge-sequence-rename-caching.sh
index 15dd2d9..56ee968 100755
--- a/t/t6429-merge-sequence-rename-caching.sh
+++ b/t/t6429-merge-sequence-rename-caching.sh
@@ -846,4 +846,64 @@
 	)
 '
 
+#
+# In the following testcase:
+#   Base:     subdir/file_1
+#   Upstream: file_1         (renamed from subdir/file)
+#   Topic_1:  subdir/file_2  (modified subdir/file)
+#   Topic_2:  subdir/file_2, file_2  (added another "file" with same contents)
+#   Topic_3:  file_2         (deleted subdir/file)
+#
+#
+# This testcase presents no problems for git traditionally, but the fact that
+#    subdir/file -> file
+# gets cached after the first pick presents a problem for the third commit
+# to be replayed, because file has contents file_2 on all three sides and
+# is thus trivially resolved early.  The point of renames is to allow us to
+# three-way merge contents across multiple filenames, but if the target is
+# already resolved, we risk throwing an assertion.  Verify that the code
+# correctly drops the irrelevant rename in order to avoid hitting that
+# assertion.
+#
+test_expect_success 'cached rename does not assert on trivially clean target' '
+	git init cached-rename-trivially-clean-target &&
+	(
+		cd cached-rename-trivially-clean-target &&
+
+		mkdir subdir &&
+		printf "%s\n" 1 2 3 >subdir/file &&
+		git add subdir/file &&
+		git commit -m orig &&
+
+		git branch upstream &&
+		git branch topic &&
+
+		git switch upstream &&
+		git mv subdir/file file &&
+		git commit -m "rename subdir/file to file" &&
+
+		git switch topic &&
+
+		echo 4 >>subdir/file &&
+		git add subdir/file &&
+		git commit -m "modify subdir/file" &&
+
+		cp subdir/file file &&
+		git add file &&
+		git commit -m "copy subdir/file to file" &&
+
+		git rm subdir/file &&
+		git commit -m "delete subdir/file" &&
+
+		git switch upstream &&
+		git replay --onto HEAD upstream..topic &&
+		git checkout topic &&
+
+		git ls-files >tracked-files &&
+		test_line_count = 1 tracked-files &&
+		printf "%s\n" 1 2 3 4 >expect &&
+		test_cmp expect file
+	)
+'
+
 test_done
diff --git a/t/t6600-test-reach.sh b/t/t6600-test-reach.sh
index 2613075..b5b314e 100755
--- a/t/t6600-test-reach.sh
+++ b/t/t6600-test-reach.sh
@@ -612,6 +612,51 @@
 		--format="%(refname)" --stdin
 '
 
+test_expect_success 'for-each-ref merged:duplicate, all reachable' '
+	git branch dup-a commit-3-3 &&
+	git branch dup-b commit-3-3 &&
+	cat >input <<-\EOF &&
+	refs/heads/commit-1-1
+	refs/heads/dup-a
+	refs/heads/dup-b
+	EOF
+	cat >expect <<-\EOF &&
+	refs/heads/commit-1-1
+	refs/heads/dup-a
+	refs/heads/dup-b
+	EOF
+	run_all_modes git for-each-ref --merged=commit-5-5 \
+		--format="%(refname)" --stdin
+'
+
+test_expect_success 'for-each-ref merged:duplicate, none reachable' '
+	cat >input <<-\EOF &&
+	refs/heads/dup-a
+	refs/heads/dup-b
+	refs/heads/commit-9-9
+	EOF
+	>expect &&
+	run_all_modes git for-each-ref --merged=commit-2-2 \
+		--format="%(refname)" --stdin
+'
+
+test_expect_success 'for-each-ref merged:duplicate at min generation' '
+	git branch dup-c commit-1-1 &&
+	git branch dup-d commit-1-1 &&
+	cat >input <<-\EOF &&
+	refs/heads/dup-c
+	refs/heads/dup-d
+	refs/heads/commit-5-5
+	EOF
+	cat >expect <<-\EOF &&
+	refs/heads/commit-5-5
+	refs/heads/dup-c
+	refs/heads/dup-d
+	EOF
+	run_all_modes git for-each-ref --merged=commit-5-5 \
+		--format="%(refname)" --stdin
+'
+
 # For get_branch_base_for_tip, we only care about
 # first-parent history. Here is the test graph with
 # second parents removed:
@@ -837,4 +882,89 @@
 		--first-parent --exclude-first-parent-only
 '
 
+test_expect_success 'rev-list --maximal-only matches merge-base --independent' '
+	# Mix of independent and dependent
+	git merge-base --independent \
+		refs/heads/commit-5-2 \
+		refs/heads/commit-3-2 \
+		refs/heads/commit-2-5 >expect &&
+	sort expect >expect.sorted &&
+	git rev-list --maximal-only \
+		refs/heads/commit-5-2 \
+		refs/heads/commit-3-2 \
+		refs/heads/commit-2-5 >actual &&
+	sort actual >actual.sorted &&
+	test_cmp expect.sorted actual.sorted &&
+
+	# All independent commits.
+	git merge-base --independent \
+		refs/heads/commit-5-2 \
+		refs/heads/commit-4-3 \
+		refs/heads/commit-3-4 \
+		refs/heads/commit-2-5 >expect &&
+	sort expect >expect.sorted &&
+	git rev-list --maximal-only \
+		refs/heads/commit-5-2 \
+		refs/heads/commit-4-3 \
+		refs/heads/commit-3-4 \
+		refs/heads/commit-2-5 >actual &&
+	sort actual >actual.sorted &&
+	test_cmp expect.sorted actual.sorted &&
+
+	# Only one independent.
+	git merge-base --independent \
+		refs/heads/commit-1-1 \
+		refs/heads/commit-4-2 \
+		refs/heads/commit-4-4 \
+		refs/heads/commit-8-4 >expect &&
+	sort expect >expect.sorted &&
+	git rev-list --maximal-only \
+		refs/heads/commit-1-1 \
+		refs/heads/commit-4-2 \
+		refs/heads/commit-4-4 \
+		refs/heads/commit-8-4 >actual &&
+	sort actual >actual.sorted &&
+	test_cmp expect.sorted actual.sorted
+'
+
+# The following tests verify the early-exit optimisation in
+# paint_down_to_common when merge-base is invoked without --all.
+# Each test checks all four commit-graph configurations.
+
+merge_base_all_modes () {
+	test_when_finished rm -rf .git/objects/info/commit-graph &&
+	git merge-base "$@" >actual &&
+	test_cmp expect actual &&
+	cp commit-graph-full .git/objects/info/commit-graph &&
+	git merge-base "$@" >actual &&
+	test_cmp expect actual &&
+	cp commit-graph-half .git/objects/info/commit-graph &&
+	git merge-base "$@" >actual &&
+	test_cmp expect actual &&
+	cp commit-graph-no-gdat .git/objects/info/commit-graph &&
+	git merge-base "$@" >actual &&
+	test_cmp expect actual
+}
+
+test_expect_success 'merge-base without --all (unique base)' '
+	git rev-parse commit-5-3 >expect &&
+	merge_base_all_modes commit-5-7 commit-8-3
+'
+
+test_expect_success 'merge-base without --all is one of --all results' '
+	test_when_finished rm -rf .git/objects/info/commit-graph &&
+
+	cp commit-graph-full .git/objects/info/commit-graph &&
+	git merge-base --all commit-5-7 commit-4-8 commit-6-6 commit-8-3 >all &&
+	git merge-base commit-5-7 commit-4-8 commit-6-6 commit-8-3 >single &&
+	test_line_count = 1 single &&
+	grep -F -f single all &&
+
+	cp commit-graph-half .git/objects/info/commit-graph &&
+	git merge-base --all commit-5-7 commit-4-8 commit-6-6 commit-8-3 >all &&
+	git merge-base commit-5-7 commit-4-8 commit-6-6 commit-8-3 >single &&
+	test_line_count = 1 single &&
+	grep -F -f single all
+'
+
 test_done
diff --git a/t/t6601-path-walk.sh b/t/t6601-path-walk.sh
index 56bd1e3..e9fcd85 100755
--- a/t/t6601-path-walk.sh
+++ b/t/t6601-path-walk.sh
@@ -7,17 +7,15 @@
 test_expect_success 'setup test repository' '
 	git checkout -b base &&
 
-	# Make some objects that will only be reachable
-	# via non-commit tags.
-	mkdir child &&
-	echo file >child/file &&
-	git add child &&
-	git commit -m "will abandon" &&
-	git tag -a -m "tree" tree-tag HEAD^{tree} &&
-	echo file2 >file2 &&
-	git add file2 &&
-	git commit --amend -m "will abandon" &&
-	git tag tree-tag2 HEAD^{tree} &&
+	# Create tree objects that are only reachable via tags,
+	# not from any commit in the history.
+	child_blob_oid=$(echo "child blob content" | git hash-object -t blob -w --stdin) &&
+	child_tree_oid=$(printf "100644 blob %s\tfile\n" "$child_blob_oid" | git mktree) &&
+	tree_tag_oid=$(printf "040000 tree %s\tchild\n" "$child_tree_oid" | git mktree) &&
+	git tag -a -m "tree" tree-tag "$tree_tag_oid" &&
+	file2_blob_oid=$(echo "tagged tree file2" | git hash-object -t blob -w --stdin) &&
+	tree_tag2_oid=$(printf "040000 tree %s\tchild\n100644 blob %s\tfile2\n" "$child_tree_oid" "$file2_blob_oid" | git mktree) &&
+	git tag tree-tag2 "$tree_tag2_oid" &&
 
 	echo blob >file &&
 	blob_oid=$(git hash-object -t blob -w --stdin <file) &&
@@ -26,7 +24,7 @@
 	blob2_oid=$(git hash-object -t blob -w --stdin <file2) &&
 	git tag blob-tag2 "$blob2_oid" &&
 
-	rm -fr child file file2 &&
+	rm -fr file file2 &&
 
 	mkdir left &&
 	mkdir right &&
@@ -34,7 +32,7 @@
 	echo b >left/b &&
 	echo c >right/c &&
 	git add . &&
-	git commit --amend -m "first" &&
+	git commit -m "first" &&
 	git tag -m "first" first HEAD &&
 
 	echo d >right/d &&
@@ -79,23 +77,23 @@
 	3:tree::$(git rev-parse base^{tree})
 	3:tree::$(git rev-parse base~1^{tree})
 	3:tree::$(git rev-parse base~2^{tree})
-	3:tree::$(git rev-parse refs/tags/tree-tag^{})
-	3:tree::$(git rev-parse refs/tags/tree-tag2^{})
 	4:blob:a:$(git rev-parse base~2:a)
-	5:blob:file2:$(git rev-parse refs/tags/tree-tag2^{}:file2)
-	6:tree:a/:$(git rev-parse base:a)
-	7:tree:child/:$(git rev-parse refs/tags/tree-tag:child)
-	8:blob:child/file:$(git rev-parse refs/tags/tree-tag:child/file)
-	9:tree:left/:$(git rev-parse base:left)
-	9:tree:left/:$(git rev-parse base~2:left)
-	10:blob:left/b:$(git rev-parse base~2:left/b)
-	10:blob:left/b:$(git rev-parse base:left/b)
-	11:tree:right/:$(git rev-parse topic:right)
-	11:tree:right/:$(git rev-parse base~1:right)
-	11:tree:right/:$(git rev-parse base~2:right)
-	12:blob:right/c:$(git rev-parse base~2:right/c)
-	12:blob:right/c:$(git rev-parse topic:right/c)
-	13:blob:right/d:$(git rev-parse base~1:right/d)
+	5:tree:/tagged-trees:$(git rev-parse refs/tags/tree-tag^{})
+	5:tree:/tagged-trees:$(git rev-parse refs/tags/tree-tag2^{})
+	6:blob:file2:$(git rev-parse refs/tags/tree-tag2^{}:file2)
+	7:tree:a/:$(git rev-parse base:a)
+	8:tree:child/:$(git rev-parse refs/tags/tree-tag:child)
+	9:blob:child/file:$(git rev-parse refs/tags/tree-tag:child/file)
+	10:tree:left/:$(git rev-parse base:left)
+	10:tree:left/:$(git rev-parse base~2:left)
+	11:blob:left/b:$(git rev-parse base~2:left/b)
+	11:blob:left/b:$(git rev-parse base:left/b)
+	12:tree:right/:$(git rev-parse topic:right)
+	12:tree:right/:$(git rev-parse base~1:right)
+	12:tree:right/:$(git rev-parse base~2:right)
+	13:blob:right/c:$(git rev-parse base~2:right/c)
+	13:blob:right/c:$(git rev-parse topic:right/c)
+	14:blob:right/d:$(git rev-parse base~1:right/d)
 	blobs:10
 	commits:4
 	tags:7
@@ -206,6 +204,43 @@
 	test_cmp_sorted expect out
 '
 
+test_expect_success 'base & topic, sparse, no tree pruning' '
+	cat >patterns <<-EOF &&
+	/*
+	!/*/
+	/left/
+	EOF
+
+	test-tool path-walk --stdin-pl --no-pl-sparse-trees \
+		-- base topic <patterns >out &&
+
+	cat >expect <<-EOF &&
+	0:commit::$(git rev-parse topic)
+	0:commit::$(git rev-parse base)
+	0:commit::$(git rev-parse base~1)
+	0:commit::$(git rev-parse base~2)
+	1:tree::$(git rev-parse topic^{tree})
+	1:tree::$(git rev-parse base^{tree})
+	1:tree::$(git rev-parse base~1^{tree})
+	1:tree::$(git rev-parse base~2^{tree})
+	2:blob:a:$(git rev-parse base~2:a)
+	3:tree:a/:$(git rev-parse base:a)
+	4:tree:left/:$(git rev-parse base:left)
+	4:tree:left/:$(git rev-parse base~2:left)
+	5:blob:left/b:$(git rev-parse base~2:left/b)
+	5:blob:left/b:$(git rev-parse base:left/b)
+	6:tree:right/:$(git rev-parse topic:right)
+	6:tree:right/:$(git rev-parse base~1:right)
+	6:tree:right/:$(git rev-parse base~2:right)
+	blobs:3
+	commits:4
+	tags:0
+	trees:10
+	EOF
+
+	test_cmp_sorted expect out
+'
+
 test_expect_success 'topic only' '
 	test-tool path-walk -- topic >out &&
 
@@ -415,4 +450,483 @@
 	test_line_count = 1 out-filtered
 '
 
+test_expect_success 'all, blob:none filter' '
+	test-tool path-walk --filter=blob:none -- --all >out &&
+
+	cat >expect <<-EOF &&
+	0:commit::$(git rev-parse topic)
+	0:commit::$(git rev-parse base)
+	0:commit::$(git rev-parse base~1)
+	0:commit::$(git rev-parse base~2)
+	1:tag:/tags:$(git rev-parse refs/tags/first)
+	1:tag:/tags:$(git rev-parse refs/tags/second.1)
+	1:tag:/tags:$(git rev-parse refs/tags/second.2)
+	1:tag:/tags:$(git rev-parse refs/tags/third)
+	1:tag:/tags:$(git rev-parse refs/tags/fourth)
+	1:tag:/tags:$(git rev-parse refs/tags/tree-tag)
+	1:tag:/tags:$(git rev-parse refs/tags/blob-tag)
+	2:blob:/tagged-blobs:$(git rev-parse refs/tags/blob-tag^{})
+	2:blob:/tagged-blobs:$(git rev-parse refs/tags/blob-tag2^{})
+	3:tree::$(git rev-parse topic^{tree})
+	3:tree::$(git rev-parse base^{tree})
+	3:tree::$(git rev-parse base~1^{tree})
+	3:tree::$(git rev-parse base~2^{tree})
+	4:tree:/tagged-trees:$(git rev-parse refs/tags/tree-tag^{})
+	4:tree:/tagged-trees:$(git rev-parse refs/tags/tree-tag2^{})
+	5:tree:a/:$(git rev-parse base:a)
+	6:tree:child/:$(git rev-parse refs/tags/tree-tag:child)
+	7:tree:left/:$(git rev-parse base:left)
+	7:tree:left/:$(git rev-parse base~2:left)
+	8:tree:right/:$(git rev-parse topic:right)
+	8:tree:right/:$(git rev-parse base~1:right)
+	8:tree:right/:$(git rev-parse base~2:right)
+	blobs:2
+	commits:4
+	tags:7
+	trees:13
+	EOF
+
+	test_cmp_sorted expect out
+'
+
+test_expect_success 'topic only, blob:none filter' '
+	test-tool path-walk --filter=blob:none -- topic >out &&
+
+	cat >expect <<-EOF &&
+	0:commit::$(git rev-parse topic)
+	0:commit::$(git rev-parse base~1)
+	0:commit::$(git rev-parse base~2)
+	1:tree::$(git rev-parse topic^{tree})
+	1:tree::$(git rev-parse base~1^{tree})
+	1:tree::$(git rev-parse base~2^{tree})
+	2:tree:left/:$(git rev-parse base~2:left)
+	3:tree:right/:$(git rev-parse topic:right)
+	3:tree:right/:$(git rev-parse base~1:right)
+	3:tree:right/:$(git rev-parse base~2:right)
+	blobs:0
+	commits:3
+	tags:0
+	trees:7
+	EOF
+
+	test_cmp_sorted expect out
+'
+
+test_expect_success 'all, blob:limit=0 filter' '
+	test-tool path-walk --filter=blob:limit=0 -- --all >out &&
+
+	cat >expect <<-EOF &&
+	0:commit::$(git rev-parse topic)
+	0:commit::$(git rev-parse base)
+	0:commit::$(git rev-parse base~1)
+	0:commit::$(git rev-parse base~2)
+	1:tag:/tags:$(git rev-parse refs/tags/first)
+	1:tag:/tags:$(git rev-parse refs/tags/second.1)
+	1:tag:/tags:$(git rev-parse refs/tags/second.2)
+	1:tag:/tags:$(git rev-parse refs/tags/third)
+	1:tag:/tags:$(git rev-parse refs/tags/fourth)
+	1:tag:/tags:$(git rev-parse refs/tags/tree-tag)
+	1:tag:/tags:$(git rev-parse refs/tags/blob-tag)
+	2:blob:/tagged-blobs:$(git rev-parse refs/tags/blob-tag^{})
+	2:blob:/tagged-blobs:$(git rev-parse refs/tags/blob-tag2^{})
+	3:tree::$(git rev-parse topic^{tree})
+	3:tree::$(git rev-parse base^{tree})
+	3:tree::$(git rev-parse base~1^{tree})
+	3:tree::$(git rev-parse base~2^{tree})
+	4:tree:/tagged-trees:$(git rev-parse refs/tags/tree-tag^{})
+	4:tree:/tagged-trees:$(git rev-parse refs/tags/tree-tag2^{})
+	5:tree:a/:$(git rev-parse base:a)
+	6:tree:child/:$(git rev-parse refs/tags/tree-tag:child)
+	7:tree:left/:$(git rev-parse base:left)
+	7:tree:left/:$(git rev-parse base~2:left)
+	8:tree:right/:$(git rev-parse topic:right)
+	8:tree:right/:$(git rev-parse base~1:right)
+	8:tree:right/:$(git rev-parse base~2:right)
+	blobs:2
+	commits:4
+	tags:7
+	trees:13
+	EOF
+
+	test_cmp_sorted expect out
+'
+
+test_expect_success 'all, blob:limit=3 filter' '
+	test-tool path-walk --filter=blob:limit=3 -- --all >out &&
+
+	cat >expect <<-EOF &&
+	0:commit::$(git rev-parse topic)
+	0:commit::$(git rev-parse base)
+	0:commit::$(git rev-parse base~1)
+	0:commit::$(git rev-parse base~2)
+	1:tag:/tags:$(git rev-parse refs/tags/first)
+	1:tag:/tags:$(git rev-parse refs/tags/second.1)
+	1:tag:/tags:$(git rev-parse refs/tags/second.2)
+	1:tag:/tags:$(git rev-parse refs/tags/third)
+	1:tag:/tags:$(git rev-parse refs/tags/fourth)
+	1:tag:/tags:$(git rev-parse refs/tags/tree-tag)
+	1:tag:/tags:$(git rev-parse refs/tags/blob-tag)
+	2:blob:/tagged-blobs:$(git rev-parse refs/tags/blob-tag^{})
+	2:blob:/tagged-blobs:$(git rev-parse refs/tags/blob-tag2^{})
+	3:tree::$(git rev-parse topic^{tree})
+	3:tree::$(git rev-parse base^{tree})
+	3:tree::$(git rev-parse base~1^{tree})
+	3:tree::$(git rev-parse base~2^{tree})
+	4:blob:a:$(git rev-parse base~2:a)
+	5:tree:/tagged-trees:$(git rev-parse refs/tags/tree-tag^{})
+	5:tree:/tagged-trees:$(git rev-parse refs/tags/tree-tag2^{})
+	6:tree:a/:$(git rev-parse base:a)
+	7:tree:child/:$(git rev-parse refs/tags/tree-tag:child)
+	8:tree:left/:$(git rev-parse base:left)
+	8:tree:left/:$(git rev-parse base~2:left)
+	9:blob:left/b:$(git rev-parse base~2:left/b)
+	10:tree:right/:$(git rev-parse topic:right)
+	10:tree:right/:$(git rev-parse base~1:right)
+	10:tree:right/:$(git rev-parse base~2:right)
+	11:blob:right/c:$(git rev-parse base~2:right/c)
+	12:blob:right/d:$(git rev-parse base~1:right/d)
+	blobs:6
+	commits:4
+	tags:7
+	trees:13
+	EOF
+
+	test_cmp_sorted expect out
+'
+
+test_expect_success 'all, tree:0 filter' '
+	test-tool path-walk --filter=tree:0 -- --all >out &&
+
+	cat >expect <<-EOF &&
+	0:commit::$(git rev-parse topic)
+	0:commit::$(git rev-parse base)
+	0:commit::$(git rev-parse base~1)
+	0:commit::$(git rev-parse base~2)
+	1:tag:/tags:$(git rev-parse refs/tags/first)
+	1:tag:/tags:$(git rev-parse refs/tags/second.1)
+	1:tag:/tags:$(git rev-parse refs/tags/second.2)
+	1:tag:/tags:$(git rev-parse refs/tags/third)
+	1:tag:/tags:$(git rev-parse refs/tags/fourth)
+	1:tag:/tags:$(git rev-parse refs/tags/tree-tag)
+	1:tag:/tags:$(git rev-parse refs/tags/blob-tag)
+	2:blob:/tagged-blobs:$(git rev-parse refs/tags/blob-tag^{})
+	2:blob:/tagged-blobs:$(git rev-parse refs/tags/blob-tag2^{})
+	3:tree:/tagged-trees:$(git rev-parse refs/tags/tree-tag^{tree})
+	3:tree:/tagged-trees:$(git rev-parse refs/tags/tree-tag2)
+	blobs:2
+	commits:4
+	tags:7
+	trees:2
+	EOF
+
+	test_cmp_sorted expect out
+'
+
+test_expect_success 'topic only, tree:0 filter' '
+	test-tool path-walk --filter=tree:0 -- topic >out &&
+
+	cat >expect <<-EOF &&
+	0:commit::$(git rev-parse topic)
+	0:commit::$(git rev-parse base~1)
+	0:commit::$(git rev-parse base~2)
+	blobs:0
+	commits:3
+	tags:0
+	trees:0
+	EOF
+
+	test_cmp_sorted expect out
+'
+
+test_expect_success 'tree:1 filter is rejected' '
+	test_must_fail test-tool path-walk --filter=tree:1 -- --all 2>err &&
+	test_grep "tree:1 filter not supported by the path-walk API" err
+'
+
+test_expect_success 'all, object:type=commit filter' '
+	test-tool path-walk --filter=object:type=commit -- --all >out &&
+
+	cat >expect <<-EOF &&
+	0:commit::$(git rev-parse topic)
+	0:commit::$(git rev-parse base)
+	0:commit::$(git rev-parse base~1)
+	0:commit::$(git rev-parse base~2)
+	blobs:0
+	commits:4
+	tags:0
+	trees:0
+	EOF
+
+	test_cmp_sorted expect out
+'
+
+test_expect_success 'all, object:type=tag filter' '
+	test-tool path-walk --filter=object:type=tag -- --all >out &&
+
+	cat >expect <<-EOF &&
+	0:tag:/tags:$(git rev-parse refs/tags/first)
+	0:tag:/tags:$(git rev-parse refs/tags/second.1)
+	0:tag:/tags:$(git rev-parse refs/tags/second.2)
+	0:tag:/tags:$(git rev-parse refs/tags/third)
+	0:tag:/tags:$(git rev-parse refs/tags/fourth)
+	0:tag:/tags:$(git rev-parse refs/tags/tree-tag)
+	0:tag:/tags:$(git rev-parse refs/tags/blob-tag)
+	blobs:0
+	commits:0
+	tags:7
+	trees:0
+	EOF
+
+	test_cmp_sorted expect out
+'
+
+test_expect_success 'all, object:type=tree filter' '
+	test-tool path-walk --filter=object:type=tree -- --all >out &&
+
+	cat >expect <<-EOF &&
+	0:tree::$(git rev-parse topic^{tree})
+	0:tree::$(git rev-parse base^{tree})
+	0:tree::$(git rev-parse base~1^{tree})
+	0:tree::$(git rev-parse base~2^{tree})
+	1:tree:/tagged-trees:$(git rev-parse refs/tags/tree-tag^{})
+	1:tree:/tagged-trees:$(git rev-parse refs/tags/tree-tag2^{})
+	2:tree:a/:$(git rev-parse base:a)
+	3:tree:child/:$(git rev-parse refs/tags/tree-tag:child)
+	4:tree:left/:$(git rev-parse base:left)
+	4:tree:left/:$(git rev-parse base~2:left)
+	5:tree:right/:$(git rev-parse topic:right)
+	5:tree:right/:$(git rev-parse base~1:right)
+	5:tree:right/:$(git rev-parse base~2:right)
+	blobs:0
+	commits:0
+	tags:0
+	trees:13
+	EOF
+
+	test_cmp_sorted expect out
+'
+
+test_expect_success 'all, object:type=blob filter' '
+	test-tool path-walk --filter=object:type=blob -- --all >out &&
+
+	cat >expect <<-EOF &&
+	0:blob:/tagged-blobs:$(git rev-parse refs/tags/blob-tag^{})
+	0:blob:/tagged-blobs:$(git rev-parse refs/tags/blob-tag2^{})
+	1:blob:a:$(git rev-parse base~2:a)
+	2:blob:left/b:$(git rev-parse base:left/b)
+	2:blob:left/b:$(git rev-parse base~2:left/b)
+	3:blob:right/c:$(git rev-parse base~2:right/c)
+	3:blob:right/c:$(git rev-parse topic:right/c)
+	4:blob:right/d:$(git rev-parse base~1:right/d)
+	blobs:8
+	commits:0
+	tags:0
+	trees:0
+	EOF
+
+	test_cmp_sorted expect out
+'
+
+test_expect_success 'all, combine:blob:none+tree:0 filter' '
+	test-tool path-walk \
+		--filter=combine:blob:none+tree:0 -- --all >out &&
+
+	cat >expect <<-EOF &&
+	0:commit::$(git rev-parse topic)
+	0:commit::$(git rev-parse base)
+	0:commit::$(git rev-parse base~1)
+	0:commit::$(git rev-parse base~2)
+	1:tag:/tags:$(git rev-parse refs/tags/first)
+	1:tag:/tags:$(git rev-parse refs/tags/second.1)
+	1:tag:/tags:$(git rev-parse refs/tags/second.2)
+	1:tag:/tags:$(git rev-parse refs/tags/third)
+	1:tag:/tags:$(git rev-parse refs/tags/fourth)
+	1:tag:/tags:$(git rev-parse refs/tags/tree-tag)
+	1:tag:/tags:$(git rev-parse refs/tags/blob-tag)
+	2:blob:/tagged-blobs:$(git rev-parse refs/tags/blob-tag^{})
+	2:blob:/tagged-blobs:$(git rev-parse refs/tags/blob-tag2^{})
+	3:tree:/tagged-trees:$(git rev-parse refs/tags/tree-tag^{tree})
+	3:tree:/tagged-trees:$(git rev-parse refs/tags/tree-tag2)
+	blobs:2
+	commits:4
+	tags:7
+	trees:2
+	EOF
+
+	test_cmp_sorted expect out
+'
+
+test_expect_success 'all, combine:object:type=blob+blob:limit=3 filter' '
+	test-tool path-walk \
+		--filter=combine:object:type=blob+blob:limit=3 \
+		-- --all >out &&
+
+	cat >expect <<-EOF &&
+	0:blob:/tagged-blobs:$(git rev-parse refs/tags/blob-tag^{})
+	0:blob:/tagged-blobs:$(git rev-parse refs/tags/blob-tag2^{})
+	1:blob:a:$(git rev-parse base~2:a)
+	2:blob:left/b:$(git rev-parse base~2:left/b)
+	3:blob:right/c:$(git rev-parse base~2:right/c)
+	4:blob:right/d:$(git rev-parse base~1:right/d)
+	blobs:6
+	commits:0
+	tags:0
+	trees:0
+	EOF
+
+	test_cmp_sorted expect out
+'
+
+test_expect_success 'all, combine of disjoint object:types is empty' '
+	test-tool path-walk \
+		--filter=combine:object:type=blob+object:type=tree \
+		-- --all >out &&
+
+	cat >expect <<-EOF &&
+	blobs:0
+	commits:0
+	tags:0
+	trees:0
+	EOF
+
+	test_cmp_sorted expect out
+'
+
+test_expect_success 'combine: rejects unsupported subfilters' '
+	test_must_fail test-tool path-walk \
+		--filter=combine:tree:1+blob:none -- --all 2>err &&
+	test_grep "tree:1 filter not supported by the path-walk API" err
+'
+
+test_expect_success 'setup sparse filter blob' '
+	# Cone-mode patterns: include root, exclude all dirs, include left/
+	cat >patterns <<-\EOF &&
+	/*
+	!/*/
+	/left/
+	EOF
+	sparse_oid=$(git hash-object -w -t blob patterns)
+'
+
+test_expect_success 'all, sparse:oid filter' '
+	test-tool path-walk --filter=sparse:oid=$sparse_oid -- --all >out &&
+
+	cat >expect <<-EOF &&
+	0:commit::$(git rev-parse topic)
+	0:commit::$(git rev-parse base)
+	0:commit::$(git rev-parse base~1)
+	0:commit::$(git rev-parse base~2)
+	1:tag:/tags:$(git rev-parse refs/tags/first)
+	1:tag:/tags:$(git rev-parse refs/tags/second.1)
+	1:tag:/tags:$(git rev-parse refs/tags/second.2)
+	1:tag:/tags:$(git rev-parse refs/tags/third)
+	1:tag:/tags:$(git rev-parse refs/tags/fourth)
+	1:tag:/tags:$(git rev-parse refs/tags/tree-tag)
+	1:tag:/tags:$(git rev-parse refs/tags/blob-tag)
+	2:blob:/tagged-blobs:$(git rev-parse refs/tags/blob-tag^{})
+	2:blob:/tagged-blobs:$(git rev-parse refs/tags/blob-tag2^{})
+	3:tree::$(git rev-parse topic^{tree})
+	3:tree::$(git rev-parse base^{tree})
+	3:tree::$(git rev-parse base~1^{tree})
+	3:tree::$(git rev-parse base~2^{tree})
+	4:blob:a:$(git rev-parse base~2:a)
+	5:tree:/tagged-trees:$(git rev-parse refs/tags/tree-tag^{})
+	5:tree:/tagged-trees:$(git rev-parse refs/tags/tree-tag2^{})
+	6:blob:file2:$(git rev-parse refs/tags/tree-tag2^{}:file2)
+	7:tree:a/:$(git rev-parse base:a)
+	8:tree:child/:$(git rev-parse refs/tags/tree-tag:child)
+	9:tree:left/:$(git rev-parse base:left)
+	9:tree:left/:$(git rev-parse base~2:left)
+	10:blob:left/b:$(git rev-parse base~2:left/b)
+	10:blob:left/b:$(git rev-parse base:left/b)
+	11:tree:right/:$(git rev-parse topic:right)
+	11:tree:right/:$(git rev-parse base~1:right)
+	11:tree:right/:$(git rev-parse base~2:right)
+	blobs:6
+	commits:4
+	tags:7
+	trees:13
+	EOF
+
+	test_cmp_sorted expect out
+'
+
+test_expect_success 'topic only, sparse:oid filter' '
+	test-tool path-walk --filter=sparse:oid=$sparse_oid -- topic >out &&
+
+	cat >expect <<-EOF &&
+	0:commit::$(git rev-parse topic)
+	0:commit::$(git rev-parse base~1)
+	0:commit::$(git rev-parse base~2)
+	1:tree::$(git rev-parse topic^{tree})
+	1:tree::$(git rev-parse base~1^{tree})
+	1:tree::$(git rev-parse base~2^{tree})
+	2:blob:a:$(git rev-parse base~2:a)
+	3:tree:left/:$(git rev-parse base~2:left)
+	4:blob:left/b:$(git rev-parse base~2:left/b)
+	5:tree:right/:$(git rev-parse topic:right)
+	5:tree:right/:$(git rev-parse base~1:right)
+	5:tree:right/:$(git rev-parse base~2:right)
+	blobs:2
+	commits:3
+	tags:0
+	trees:7
+	EOF
+
+	test_cmp_sorted expect out
+'
+
+# Demonstrate the SEEN flag ordering issue: when the same tree/blob OID
+# appears at two sibling paths where one is in-cone and the other is
+# out-of-cone, the path-walk must still discover blobs at the in-cone
+# path even when the shared tree OID was first encountered out-of-cone.
+# Since sparse:oid includes all trees, the out-of-cone tree (aaa/) is
+# walked first, and its blob is skipped. The path-walk then re-walks
+# the same tree OID at the in-cone path (zzz/) to find the blob there.
+
+test_expect_success 'setup shared tree OID across cone boundary' '
+	git checkout --orphan shared-tree &&
+	git rm -rf . &&
+	mkdir aaa zzz &&
+	echo "shared content" >aaa/file &&
+	echo "shared content" >zzz/file &&
+	echo "root file" >rootfile &&
+	git add aaa zzz rootfile &&
+	git commit -m "aaa and zzz have same tree OID" &&
+
+	# Verify they really share a tree OID
+	aaa_tree=$(git rev-parse HEAD:aaa) &&
+	zzz_tree=$(git rev-parse HEAD:zzz) &&
+	test "$aaa_tree" = "$zzz_tree" &&
+
+	# Cone pattern: include root + zzz/ (not aaa/)
+	cat >shared-patterns <<-\EOF &&
+	/*
+	!/*/
+	/zzz/
+	EOF
+	shared_sparse_oid=$(git hash-object -w -t blob shared-patterns)
+'
+
+test_expect_success 'sparse:oid with shared tree OID across cone boundary' '
+	test-tool path-walk \
+		--filter=sparse:oid=$shared_sparse_oid \
+		-- shared-tree >out &&
+
+	cat >expect <<-EOF &&
+	0:commit::$(git rev-parse shared-tree)
+	1:tree::$(git rev-parse shared-tree^{tree})
+	2:blob:rootfile:$(git rev-parse shared-tree:rootfile)
+	3:tree:aaa/:$(git rev-parse shared-tree:aaa)
+	4:tree:zzz/:$(git rev-parse shared-tree:zzz)
+	5:blob:zzz/file:$(git rev-parse shared-tree:zzz/file)
+	blobs:2
+	commits:1
+	tags:0
+	trees:3
+	EOF
+
+	test_cmp_sorted expect out
+'
+
 test_done
diff --git a/t/t7004-tag.sh b/t/t7004-tag.sh
index ce2ff2a..d918005 100755
--- a/t/t7004-tag.sh
+++ b/t/t7004-tag.sh
@@ -33,8 +33,10 @@
 '
 
 test_expect_success 'listing all tags in an empty tree should output nothing' '
-	test $(git tag -l | wc -l) -eq 0 &&
-	test $(git tag | wc -l) -eq 0
+	git tag -l >actual &&
+	test_must_be_empty actual &&
+	git tag >actual &&
+	test_must_be_empty actual
 '
 
 test_expect_success 'sort tags, ignore case' '
@@ -143,9 +145,7 @@
 '
 
 test_expect_success 'Multiple -l or --list options are equivalent to one -l option' '
-	cat >expect <<-\EOF &&
-	mytag
-	EOF
+	git tag -l >expect &&
 	git tag -l -l >actual &&
 	test_cmp expect actual &&
 	git tag --list --list >actual &&
@@ -155,8 +155,10 @@
 '
 
 test_expect_success 'listing all tags if one exists should output that tag' '
-	test $(git tag -l) = mytag &&
-	test $(git tag) = mytag
+	git tag -l >actual &&
+	test_grep "^mytag$" actual &&
+	git tag >actual &&
+	test_grep "^mytag$" actual
 '
 
 # pattern matching:
@@ -166,11 +168,15 @@
 '
 
 test_expect_success 'listing a tag with --ignore-case' '
-	test $(git tag -l --ignore-case MYTAG) = mytag
+	echo mytag >expect &&
+	git tag -l --ignore-case MYTAG >actual &&
+	test_cmp expect actual
 '
 
 test_expect_success 'listing a tag using a matching pattern should output that tag' '
-	test $(git tag -l mytag) = mytag
+	echo mytag >expect &&
+	git tag -l mytag >actual &&
+	test_cmp expect actual
 '
 
 test_expect_success 'listing tags using a non-matching pattern should succeed' '
@@ -178,7 +184,8 @@
 '
 
 test_expect_success 'listing tags using a non-matching pattern should output nothing' '
-	test $(git tag -l xxx | wc -l) -eq 0
+	git tag -l xxx >actual &&
+	test_must_be_empty actual
 '
 
 # special cases for creating tags:
@@ -188,13 +195,14 @@
 '
 
 test_expect_success 'trying to create a tag with a non-valid name should fail' '
-	test $(git tag -l | wc -l) -eq 1 &&
+	git tag -l >tags-before &&
 	test_must_fail git tag "" &&
 	test_must_fail git tag .othertag &&
 	test_must_fail git tag "other tag" &&
 	test_must_fail git tag "othertag^" &&
 	test_must_fail git tag "other~tag" &&
-	test $(git tag -l | wc -l) -eq 1
+	git tag -l >tags-after &&
+	test_cmp tags-before tags-after
 '
 
 test_expect_success 'creating a tag using HEAD directly should succeed' '
@@ -222,12 +230,7 @@
 '
 
 test_expect_success 'trying to delete tags without params should succeed and do nothing' '
-	cat >expect <<-\EOF &&
-	myhead
-	mytag
-	EOF
-	git tag -l >actual &&
-	test_cmp expect actual &&
+	git tag -l >expect &&
 	git tag -d &&
 	git tag -l >actual &&
 	test_cmp expect actual
@@ -433,8 +436,12 @@
 
 test_expect_success 'a non-annotated tag created without parameters should point to HEAD' '
 	git tag non-annotated-tag &&
-	test $(git cat-file -t non-annotated-tag) = commit &&
-	test $(git rev-parse non-annotated-tag) = $(git rev-parse HEAD)
+	echo commit >expect &&
+	git cat-file -t non-annotated-tag >actual &&
+	test_cmp expect actual &&
+	git rev-parse HEAD >expect &&
+	git rev-parse non-annotated-tag >actual &&
+	test_cmp expect actual
 '
 
 test_expect_success 'trying to verify an unknown tag should fail' '
@@ -1523,11 +1530,11 @@
 '
 
 test_expect_success 'git tag -a fails if tag annotation is empty' '
-	! (GIT_EDITOR=cat git tag -a initial-comment)
+	test_must_fail env GIT_EDITOR=cat git tag -a initial-comment
 '
 
 test_expect_success 'message in editor has initial comment' '
-	! (GIT_EDITOR=cat git tag -a initial-comment >actual)
+	test_must_fail env GIT_EDITOR=cat git tag -a initial-comment >actual
 '
 
 test_expect_success 'message in editor has initial comment: first line' '
diff --git a/t/t7060-wtstatus.sh b/t/t7060-wtstatus.sh
index 0f4344c..942ddbb 100755
--- a/t/t7060-wtstatus.sh
+++ b/t/t7060-wtstatus.sh
@@ -9,6 +9,7 @@
 
 test_expect_success setup '
 	git config --global advice.statusuoption false &&
+	echo "/.gitconfig" >>.git/info/exclude &&
 	test_commit A &&
 	test_commit B oneside added &&
 	git checkout A^0 &&
@@ -221,7 +222,6 @@
 	git status --branch --porcelain >actual &&
 	cat >expected <<-EOF &&
 	## HEAD (no branch)
-	?? .gitconfig
 	?? actual
 	?? expect
 	?? expected
@@ -237,7 +237,6 @@
 	git status --branch --porcelain=v1 >actual &&
 	cat >expected <<-EOF &&
 	## HEAD (no branch)
-	?? .gitconfig
 	?? actual
 	?? expect
 	?? expected
diff --git a/t/t7061-wtstatus-ignore.sh b/t/t7061-wtstatus-ignore.sh
index 2f9bea9..14ddaba 100755
--- a/t/t7061-wtstatus-ignore.sh
+++ b/t/t7061-wtstatus-ignore.sh
@@ -18,6 +18,7 @@
 	: >untracked/ignored &&
 	: >untracked/uncommitted &&
 	git status --porcelain --ignored >actual &&
+	test_filter_gitconfig actual &&
 	test_cmp expected actual
 '
 
@@ -27,6 +28,7 @@
 	: >untracked/ignored &&
 	: >untracked/uncommitted &&
 	git status --porcelain --ignored >actual &&
+	test_filter_gitconfig actual &&
 	test_cmp expected actual
 '
 
@@ -40,18 +42,22 @@
 test_expect_success 'status untracked files --ignored with pathspec (literal match)' '
 	git status --porcelain --ignored -- untracked/ignored >actual &&
 	echo "!! untracked/ignored" >expected &&
+	test_filter_gitconfig actual &&
 	test_cmp expected actual &&
 	git status --porcelain --ignored -- untracked/uncommitted >actual &&
 	echo "?? untracked/uncommitted" >expected &&
+	test_filter_gitconfig actual &&
 	test_cmp expected actual
 '
 
 test_expect_success 'status untracked files --ignored with pathspec (glob match)' '
 	git status --porcelain --ignored -- untracked/i\* >actual &&
 	echo "!! untracked/ignored" >expected &&
+	test_filter_gitconfig actual &&
 	test_cmp expected actual &&
 	git status --porcelain --ignored -- untracked/u\* >actual &&
 	echo "?? untracked/uncommitted" >expected &&
+	test_filter_gitconfig actual &&
 	test_cmp expected actual
 '
 
@@ -65,6 +71,7 @@
 
 test_expect_success 'status untracked directory with --ignored -u' '
 	git status --porcelain --ignored -u >actual &&
+	test_filter_gitconfig actual &&
 	test_cmp expected actual
 '
 cat >expected <<\EOF
@@ -76,9 +83,11 @@
 	git status --porcelain --ignored >tmp &&
 	grep untracked/ tmp >actual &&
 	rm tmp &&
+	test_filter_gitconfig actual &&
 	test_cmp expected actual &&
 
 	git status --porcelain --ignored untracked/ >actual &&
+	test_filter_gitconfig actual &&
 	test_cmp expected actual
 '
 
@@ -89,6 +98,7 @@
 
 test_expect_success 'status prefixed untracked sub-directory with --ignored -u' '
 	git status --porcelain --ignored -u untracked/ >actual &&
+	test_filter_gitconfig actual &&
 	test_cmp expected actual
 '
 
@@ -104,6 +114,7 @@
 	mkdir ignored &&
 	: >ignored/uncommitted &&
 	git status --porcelain --ignored >actual &&
+	test_filter_gitconfig actual &&
 	test_cmp expected actual
 '
 
@@ -116,6 +127,7 @@
 
 test_expect_success 'status ignored directory with --ignore -u' '
 	git status --porcelain --ignored -u >actual &&
+	test_filter_gitconfig actual &&
 	test_cmp expected actual
 '
 
@@ -130,6 +142,7 @@
 	mkdir untracked-ignored &&
 	mkdir untracked-ignored/test &&
 	git status --porcelain --ignored >actual &&
+	test_filter_gitconfig actual &&
 	test_cmp expected actual
 '
 
@@ -141,6 +154,7 @@
 
 test_expect_success 'status empty untracked directory with --ignore -u' '
 	git status --porcelain --ignored -u >actual &&
+	test_filter_gitconfig actual &&
 	test_cmp expected actual
 '
 
@@ -155,6 +169,7 @@
 	: >untracked-ignored/ignored &&
 	: >untracked-ignored/test/ignored &&
 	git status --porcelain --ignored >actual &&
+	test_filter_gitconfig actual &&
 	test_cmp expected actual
 '
 
@@ -168,6 +183,7 @@
 
 test_expect_success 'status untracked directory with ignored files with --ignore -u' '
 	git status --porcelain --ignored -u >actual &&
+	test_filter_gitconfig actual &&
 	test_cmp expected actual
 '
 
@@ -185,6 +201,7 @@
 	git commit -m. &&
 	echo "tracked" >.gitignore &&
 	git status --porcelain --ignored >actual &&
+	test_filter_gitconfig actual &&
 	test_cmp expected actual
 '
 
@@ -196,6 +213,7 @@
 
 test_expect_success 'status ignored tracked directory with --ignore -u' '
 	git status --porcelain --ignored -u >actual &&
+	test_filter_gitconfig actual &&
 	test_cmp expected actual
 '
 
@@ -208,6 +226,7 @@
 test_expect_success 'status ignored tracked directory and ignored file with --ignore' '
 	echo "committed" >>.gitignore &&
 	git status --porcelain --ignored >actual &&
+	test_filter_gitconfig actual &&
 	test_cmp expected actual
 '
 
@@ -219,6 +238,7 @@
 
 test_expect_success 'status ignored tracked directory and ignored file with --ignore -u' '
 	git status --porcelain --ignored -u >actual &&
+	test_filter_gitconfig actual &&
 	test_cmp expected actual
 '
 
@@ -233,6 +253,7 @@
 	echo "tracked" >.gitignore &&
 	: >tracked/uncommitted &&
 	git status --porcelain --ignored >actual &&
+	test_filter_gitconfig actual &&
 	test_cmp expected actual
 '
 
@@ -245,6 +266,7 @@
 
 test_expect_success 'status ignored tracked directory and uncommitted file with --ignore -u' '
 	git status --porcelain --ignored -u >actual &&
+	test_filter_gitconfig actual &&
 	test_cmp expected actual
 '
 
@@ -260,6 +282,7 @@
 	mkdir tracked/ignored &&
 	: >tracked/ignored/uncommitted &&
 	git status --porcelain --ignored >actual &&
+	test_filter_gitconfig actual &&
 	test_cmp expected actual
 '
 
@@ -272,6 +295,7 @@
 
 test_expect_success 'status ignored tracked directory with uncommitted file in untracked subdir with --ignore -u' '
 	git status --porcelain --ignored -u >actual &&
+	test_filter_gitconfig actual &&
 	test_cmp expected actual
 '
 
@@ -287,6 +311,7 @@
 	git add -f tracked/ignored/committed &&
 	git commit -m. &&
 	git status --porcelain --ignored >actual &&
+	test_filter_gitconfig actual &&
 	test_cmp expected actual
 '
 
@@ -299,6 +324,7 @@
 
 test_expect_success 'status ignored tracked directory with uncommitted file in tracked subdir with --ignore -u' '
 	git status --porcelain --ignored -u >actual &&
+	test_filter_gitconfig actual &&
 	test_cmp expected actual
 '
 
@@ -310,6 +336,7 @@
 	git init tracked/submodule &&
 	test_commit -C tracked/submodule initial &&
 	git status --porcelain --ignored -u tracked/submodule >actual &&
+	test_filter_gitconfig actual &&
 	test_cmp expected actual
 '
 
diff --git a/t/t7064-wtstatus-pv2.sh b/t/t7064-wtstatus-pv2.sh
index 8bbc5ce..be6c931 100755
--- a/t/t7064-wtstatus-pv2.sh
+++ b/t/t7064-wtstatus-pv2.sh
@@ -231,6 +231,7 @@
 	EOF
 
 	git status --porcelain=v2 --ignored --untracked-files=all >actual &&
+	test_filter_gitconfig actual &&
 	test_cmp expect actual
 '
 
diff --git a/t/t7104-reset-hard.sh b/t/t7104-reset-hard.sh
index 7948ec3..c23d6e3 100755
--- a/t/t7104-reset-hard.sh
+++ b/t/t7104-reset-hard.sh
@@ -21,7 +21,7 @@
 	rm -f hello &&
 	mkdir -p hello &&
 	>hello/world &&
-	test "$(git ls-files -o)" = hello/world
+	test "$(git ls-files -o --exclude-standard)" = hello/world
 
 '
 
diff --git a/t/t7201-co.sh b/t/t7201-co.sh
index 9bcf7c0..7613b1d 100755
--- a/t/t7201-co.sh
+++ b/t/t7201-co.sh
@@ -102,7 +102,10 @@
 
 	test "$(git symbolic-ref HEAD)" = "refs/heads/side" &&
 
-	printf "M\t%s\n" one >expect.messages &&
+	cat >expect.messages <<-\EOF &&
+	The following paths have local changes:
+	M	one
+	EOF
 	test_cmp expect.messages messages &&
 
 	fill "M	one" "A	three" "D	two" >expect.main &&
@@ -210,6 +213,72 @@
 	test_cmp expect two
 '
 
+test_expect_success 'checkout -m with mixed staged and unstaged changes' '
+	git checkout -f main &&
+	git clean -f &&
+
+	fill 0 x y z >same &&
+	git add same &&
+	fill 1 2 3 4 5 6 7 >one &&
+	git checkout -m side >actual 2>&1 &&
+	test_grep "Applied autostash" actual &&
+	fill 0 x y z >expect &&
+	test_cmp expect same &&
+	fill 1 2 3 4 5 6 7 >expect &&
+	test_cmp expect one
+'
+
+test_expect_success 'checkout -m creates a recoverable stash on conflict' '
+	git checkout -f main &&
+	git clean -f &&
+
+	fill 1 2 3 4 5 >one &&
+	test_must_fail git checkout side 2>stderr &&
+	test_grep "Your local changes" stderr &&
+	git checkout -m side >actual 2>&1 &&
+	test_grep "resulted in conflicts" actual &&
+	test_grep "git stash drop" actual &&
+	test_grep "git stash pop" actual &&
+	test_grep "The following paths have local changes" actual &&
+	git log -p -1 --format="%gs%n%B" -g --diff-merges=1 refs/stash >actual &&
+	sed /^index/d actual >actual.trimmed &&
+	cat >expect <<-EOF &&
+	autostash while switching to ${SQ}side${SQ}
+	On main: autostash while switching to ${SQ}side${SQ}
+
+	diff --git a/one b/one
+	--- a/one
+	+++ b/one
+	@@ -3,6 +3,3 @@
+	 3
+	 4
+	 5
+	-6
+	-7
+	-8
+	EOF
+	test_cmp expect actual.trimmed &&
+	git stash drop &&
+	git reset --hard
+'
+
+test_expect_success 'checkout -m which would overwrite untracked file' '
+	git checkout -f --detach main &&
+	test_commit another-file &&
+	git checkout HEAD^ &&
+	>another-file.t &&
+	fill 1 2 3 4 5 >one &&
+	test_must_fail git checkout -m @{-1} 2>err &&
+	q_to_tab >expect <<-\EOF &&
+	error: The following untracked working tree files would be overwritten by checkout:
+	Qanother-file.t
+	Please move or remove them before you switch branches.
+	Aborting
+	Applied autostash.
+	EOF
+	test_cmp expect err
+'
+
 test_expect_success 'switch to another branch while carrying a deletion' '
 	git checkout -f main &&
 	git reset --hard &&
diff --git a/t/t7406-submodule-update.sh b/t/t7406-submodule-update.sh
index 3adab12..6abb008 100755
--- a/t/t7406-submodule-update.sh
+++ b/t/t7406-submodule-update.sh
@@ -1055,6 +1055,14 @@
 	)
 '
 
+test_expect_success 'submodule update honors fetch jobs config from .gitmodules' '
+	test_when_finished "rm -rf super3" &&
+	git clone cloned super3 &&
+	git -C super3 config -f .gitmodules submodule.fetchJobs 67 &&
+	GIT_TRACE="$(pwd)/trace.out" git -C super3 submodule update --init &&
+	test_grep "67 tasks" trace.out
+'
+
 test_expect_success 'git clone passes the parallel jobs config on to submodules' '
 	test_when_finished "rm -rf super4" &&
 	GIT_TRACE=$(pwd)/trace.out git clone --recurse-submodules --jobs 7 . super4 &&
diff --git a/t/t7422-submodule-output.sh b/t/t7422-submodule-output.sh
index aea1ddf..852136f 100755
--- a/t/t7422-submodule-output.sh
+++ b/t/t7422-submodule-output.sh
@@ -198,7 +198,7 @@
 	(
 		cd repo &&
 		GIT_ALLOW_PROTOCOL=file git submodule add "$(pwd)"/../submodule &&
-		{ git submodule status --recursive 2>err; echo $?>status; } |
+		{ { ret=0 && git submodule status --recursive 2>err || ret=$?; } && echo $ret >status; } |
 			grep -q recursive-submodule-path-1 &&
 		test_must_be_empty err &&
 		test_match_signal 13 "$(cat status)"
diff --git a/t/t7450-bad-git-dotfiles.sh b/t/t7450-bad-git-dotfiles.sh
index f512eed..8cc8652 100755
--- a/t/t7450-bad-git-dotfiles.sh
+++ b/t/t7450-bad-git-dotfiles.sh
@@ -220,17 +220,19 @@
 		)
 	'
 
-	test -n "$refuse_index" &&
-	test_expect_success "refuse to load symlinked $name into index ($type)" '
-		test_must_fail \
-			git -C $dir \
-			    -c core.protectntfs \
-			    -c core.protecthfs \
-			    read-tree $tree 2>err &&
-		grep "invalid path.*$name" err &&
-		git -C $dir ls-files -s >out &&
-		test_must_be_empty out
-	'
+	if test -n "$refuse_index"
+	then
+		test_expect_success "refuse to load symlinked $name into index ($type)" '
+			test_must_fail \
+				git -C $dir \
+				    -c core.protectntfs \
+				    -c core.protecthfs \
+				    read-tree $tree 2>err &&
+			grep "invalid path.*$name" err &&
+			git -C $dir ls-files -s >out &&
+			test_must_be_empty out
+		'
+	fi
 }
 
 check_dotx_symlink gitmodules vanilla .gitmodules
diff --git a/t/t7508-status.sh b/t/t7508-status.sh
index a5e21bf..c2057bc 100755
--- a/t/t7508-status.sh
+++ b/t/t7508-status.sh
@@ -263,6 +263,7 @@
 	!! untracked
 	EOF
 	git status -s --ignored >output &&
+	test_filter_gitconfig output &&
 	test_cmp expect output &&
 
 	cat >expect <<\EOF &&
@@ -296,6 +297,7 @@
 
 EOF
 	git status --ignored >output &&
+	test_filter_gitconfig output &&
 	test_cmp expect output
 '
 
@@ -328,6 +330,7 @@
 	!! untracked
 	EOF
 	git status -s --ignored >output &&
+	test_filter_gitconfig output &&
 	test_cmp expect output &&
 
 	cat >expect <<\EOF &&
@@ -358,6 +361,7 @@
 
 EOF
 	git status --ignored >output &&
+	test_filter_gitconfig output &&
 	test_cmp expect output
 '
 
@@ -773,8 +777,8 @@
 '
 
 # recover unconditionally from color tests
-git config --unset color.status
-git config --unset color.ui
+git config --unset color.status || :
+git config --unset color.ui || :
 
 test_expect_success 'status --porcelain respects -b' '
 
diff --git a/t/t7510-signed-commit.sh b/t/t7510-signed-commit.sh
index 1201c85..aa9108d 100755
--- a/t/t7510-signed-commit.sh
+++ b/t/t7510-signed-commit.sh
@@ -462,4 +462,14 @@
 	git commit -S --allow-empty -m signed-commit
 '
 
+test_expect_success GPG 'commit verifies with non-UTF-8 commit message' '
+	printf "I hate\\376\\377UTF-8\\n" >message &&
+	echo unusual-message >file &&
+	git add file &&
+	test_tick && git commit -S -F message 2>err &&
+	git verify-commit HEAD &&
+	grep "commit message did not conform to UTF-8" err >lines &&
+	test_line_count = 1 lines
+'
+
 test_done
diff --git a/t/t7521-ignored-mode.sh b/t/t7521-ignored-mode.sh
index a88b02b..7ea0b0d 100755
--- a/t/t7521-ignored-mode.sh
+++ b/t/t7521-ignored-mode.sh
@@ -30,6 +30,7 @@
 		dir/ignored/ignored_1.ign dir/ignored/ignored_2.ign &&
 
 	git status --porcelain=v2 --ignored=matching --untracked-files=all >output &&
+	test_filter_gitconfig output &&
 	test_cmp expect output
 '
 
diff --git a/t/t7527-builtin-fsmonitor.sh b/t/t7527-builtin-fsmonitor.sh
index e7b4065..b63c162 100755
--- a/t/t7527-builtin-fsmonitor.sh
+++ b/t/t7527-builtin-fsmonitor.sh
@@ -10,9 +10,57 @@
 	test_done
 fi
 
+# Verify that the filesystem delivers events to the daemon.
+# On some configurations (e.g., overlayfs with older kernels),
+# inotify watches succeed but events are never delivered.  The
+# cookie wait will time out and the daemon logs a trace message.
+#
+# Use "timeout" (if available) to guard each step against hangs.
+maybe_timeout () {
+	if type timeout >/dev/null 2>&1
+	then
+		timeout "$@"
+	else
+		shift
+		"$@"
+	fi
+}
+verify_fsmonitor_works () {
+	git init test_fsmonitor_smoke || return 1
+
+	GIT_TRACE_FSMONITOR="$PWD/smoke.trace" &&
+	export GIT_TRACE_FSMONITOR &&
+	maybe_timeout 30 \
+		git -C test_fsmonitor_smoke fsmonitor--daemon start \
+			--start-timeout=10
+	ret=$?
+	unset GIT_TRACE_FSMONITOR
+	if test $ret -ne 0
+	then
+		rm -rf test_fsmonitor_smoke smoke.trace
+		return 1
+	fi
+
+	maybe_timeout 10 \
+		test-tool -C test_fsmonitor_smoke fsmonitor-client query \
+			--token 0 >/dev/null 2>&1
+	maybe_timeout 5 \
+		git -C test_fsmonitor_smoke fsmonitor--daemon stop 2>/dev/null
+	! grep -q "cookie_wait timed out" "$PWD/smoke.trace" 2>/dev/null
+	ret=$?
+	rm -rf test_fsmonitor_smoke smoke.trace
+	return $ret
+}
+
+if ! verify_fsmonitor_works
+then
+	skip_all="filesystem does not deliver fsmonitor events (container/overlayfs?)"
+	test_done
+fi
+
 stop_daemon_delete_repo () {
 	r=$1 &&
-	test_might_fail git -C $r fsmonitor--daemon stop &&
+	{ maybe_timeout 30 git -C $r fsmonitor--daemon stop 2>/dev/null || :; } &&
 	rm -rf $1
 }
 
@@ -67,7 +115,7 @@
 			export GIT_TEST_FSMONITOR_TOKEN
 		fi &&
 
-		git $r fsmonitor--daemon start &&
+		git $r fsmonitor--daemon start --start-timeout=10 &&
 		git $r fsmonitor--daemon status
 	)
 }
@@ -523,6 +571,28 @@
 	retry_grep "^event: dir1$" .git/trace
 '
 
+test_expect_success 'rapid nested directory creation' '
+	test_when_finished "git fsmonitor--daemon stop; rm -rf rapid" &&
+
+	start_daemon --tf "$PWD/.git/trace" &&
+
+	# Rapidly create nested directories to exercise race conditions
+	# where directory watches may be added concurrently during
+	# event processing and recursive scanning.
+	for i in $(test_seq 1 20)
+	do
+		mkdir -p "rapid/nested/dir$i/subdir/deep" || return 1
+	done &&
+
+	# Give the daemon time to process all events
+	sleep 1 &&
+
+	test-tool fsmonitor-client query --token 0 &&
+
+	# Verify daemon is still running (did not crash)
+	git fsmonitor--daemon status
+'
+
 # The next few test cases exercise the token-resync code.  When filesystem
 # drops events (because of filesystem velocity or because the daemon isn't
 # polling fast enough), we need to discard the cached data (relative to the
@@ -913,7 +983,10 @@
 start_git_in_background () {
 	git "$@" &
 	git_pid=$!
-	git_pgid=$(ps -o pgid= -p $git_pid)
+	git_pgid=$(ps -o pgid= -p $git_pid 2>/dev/null ||
+		awk '{print $5}' /proc/$git_pid/stat 2>/dev/null) &&
+	git_pgid="${git_pgid## }" &&
+	git_pgid="${git_pgid%% }"
 	nr_tries_left=10
 	while true
 	do
@@ -924,15 +997,16 @@
 		fi
 		sleep 1
 		nr_tries_left=$(($nr_tries_left - 1))
-	done >/dev/null 2>&1 &
+	done >/dev/null 2>&1 3>&- 4>&- 5>&- 6>&- 7>&- &
 	watchdog_pid=$!
 	wait $git_pid
 }
 
 stop_git () {
-	while kill -0 -- -$git_pgid
+	test -n "$git_pgid" || return 0
+	while kill -0 -- -$git_pgid 2>/dev/null
 	do
-		kill -- -$git_pgid
+		kill -- -$git_pgid 2>/dev/null
 		sleep 1
 	done
 }
@@ -947,7 +1021,7 @@
 
 test_expect_success !MINGW "submodule implicitly starts daemon by pull" '
 	test_atexit "stop_watchdog" &&
-	test_when_finished "stop_git; rm -rf cloned super sub" &&
+	test_when_finished "set +m; stop_git; rm -rf cloned super sub" &&
 
 	create_super super &&
 	create_sub sub &&
diff --git a/t/t7600-merge.sh b/t/t7600-merge.sh
index 9838094..f877d9a 100755
--- a/t/t7600-merge.sh
+++ b/t/t7600-merge.sh
@@ -914,7 +914,8 @@
 	git diff >expect &&
 	test_when_finished "test_might_fail git stash drop" &&
 	git merge --autostash c3 2>err &&
-	test_grep "Applying autostash resulted in conflicts." err &&
+	test_grep "applying them" err &&
+	test_grep "resulted in conflicts" err &&
 	git show HEAD:file >merge-result &&
 	test_cmp result.1-9 merge-result &&
 	git stash show -p >actual &&
diff --git a/t/t7704-repack-cruft.sh b/t/t7704-repack-cruft.sh
index aa2e2e6..9e03b04 100755
--- a/t/t7704-repack-cruft.sh
+++ b/t/t7704-repack-cruft.sh
@@ -869,4 +869,26 @@
 	)
 '
 
+test_expect_success 'repack rescues once-cruft objects above geometric split' '
+	git config repack.midxMustContainCruft false &&
+
+	test_commit reachable &&
+	test_commit unreachable &&
+
+	unreachable="$(git rev-parse HEAD)" &&
+
+	git reset --hard HEAD^ &&
+	git tag -d unreachable &&
+	git reflog expire --all --expire=all &&
+
+	git repack --cruft -d &&
+
+	echo $unreachable | git pack-objects .git/objects/pack/pack &&
+
+	test_commit new &&
+
+	git update-ref refs/heads/other $unreachable &&
+	git repack --geometric=2 -d --write-midx --write-bitmap-index
+'
+
 test_done
diff --git a/t/t7705-repack-incremental-midx.sh b/t/t7705-repack-incremental-midx.sh
new file mode 100755
index 0000000..25a8c40
--- /dev/null
+++ b/t/t7705-repack-incremental-midx.sh
@@ -0,0 +1,525 @@
+#!/bin/sh
+
+test_description='git repack --write-midx=incremental'
+
+. ./test-lib.sh
+
+GIT_TEST_MULTI_PACK_INDEX=0
+GIT_TEST_MULTI_PACK_INDEX_WRITE_BITMAP=0
+GIT_TEST_MULTI_PACK_INDEX_WRITE_INCREMENTAL=0
+
+objdir=.git/objects
+packdir=$objdir/pack
+midxdir=$packdir/multi-pack-index.d
+midx_chain=$midxdir/multi-pack-index-chain
+
+# incrementally_repack N
+#
+# Make "N" new commits, each stored in their own pack, and then repacked
+# with the --write-midx=incremental strategy.
+incrementally_repack () {
+	for i in $(test_seq 1 "$1")
+	do
+		test_commit "$i" &&
+
+		git repack --geometric=2 -d --write-midx=incremental \
+			--write-bitmap-index &&
+		git multi-pack-index verify || return 1
+	done
+}
+
+# Create packs with geometrically increasing sizes so that they
+# satisfy the geometric progression and survive a --geometric=2
+# repack without being rolled up. Creates 3 packs containing 1,
+# 2, and 6 commits (3, 6, and 18 objects) respectively.
+create_geometric_packs () {
+	test_commit "small" &&
+	git repack -d &&
+
+	test_commit_bulk --message="medium" 2 &&
+	test_commit_bulk --message="large" 6 &&
+
+	git repack --geometric=2 -d --write-midx=incremental \
+		--write-bitmap-index
+}
+
+# create_layer <test_commit_bulk args>
+#
+# Creates a new MIDX layer with the contents of "test_commit_bulk $@".
+create_layer () {
+	test_commit_bulk "$@" &&
+
+	git multi-pack-index write --incremental --bitmap
+}
+
+# create_layers
+#
+# Reads lines of "<message> <nr>" from stdin and creates a new MIDX
+# layer for each line. See create_layer above for more.
+create_layers () {
+	while read msg nr
+	do
+		create_layer --message="$msg" "$nr" || return 1
+	done
+}
+
+test_expect_success '--write-midx=incremental without --geometric' '
+	git init incremental-without-geometric &&
+	(
+		cd incremental-without-geometric &&
+
+		git config maintenance.auto false &&
+
+		test_commit first &&
+		git repack -d &&
+
+		test_commit second &&
+		git repack --write-midx=incremental &&
+
+		git multi-pack-index verify &&
+		test_line_count = 1 $midx_chain &&
+		cp $midx_chain $midx_chain.before &&
+
+		# A second repack appends a new layer without
+		# disturbing the existing one.
+		test_commit third &&
+		git repack --write-midx=incremental &&
+
+		git multi-pack-index verify &&
+		test_line_count = 2 $midx_chain &&
+		head -n 1 $midx_chain.before >expect &&
+		head -n 1 $midx_chain >actual &&
+		test_cmp expect actual &&
+
+		git fsck
+	)
+'
+
+test_expect_success 'below layer threshold, tip packs excluded' '
+	git init below-layer-threshold-tip-packs-excluded &&
+	(
+		cd below-layer-threshold-tip-packs-excluded &&
+
+		git config maintenance.auto false &&
+		git config repack.midxnewlayerthreshold 4 &&
+		git config repack.midxsplitfactor 2 &&
+
+		# Create 3 packs forming a geometric progression by
+		# object count such that they are unmodified by the
+		# initial repack. The MIDX chain thusly contains a
+		# single layer with three packs.
+		create_geometric_packs &&
+		ls $packdir/pack-*.idx | sort >packs.before &&
+		test_line_count = 1 $midx_chain &&
+		cp $midx_chain $midx_chain.before &&
+
+		# Repack a new commit. Since the layer threshold is
+		# unmet, a new MIDX layer is added on top of the
+		# existing one.
+		test_commit extra &&
+		git repack --geometric=2 -d --write-midx=incremental \
+			--write-bitmap-index &&
+		git multi-pack-index verify &&
+
+		ls $packdir/pack-*.idx | sort >packs.after &&
+		comm -13 packs.before packs.after >packs.new &&
+		test_line_count = 1 packs.new &&
+
+		test_line_count = 2 "$midx_chain" &&
+		head -n 1 "$midx_chain.before" >expect &&
+		head -n 1 "$midx_chain" >actual &&
+		test_cmp expect actual
+	)
+'
+
+test_expect_success 'above layer threshold, tip packs repacked' '
+	git init above-layer-threshold-tip-packs-repacked &&
+	(
+		cd above-layer-threshold-tip-packs-repacked &&
+
+		git config maintenance.auto false &&
+		git config repack.midxnewlayerthreshold 2 &&
+		git config repack.midxsplitfactor 2 &&
+
+		# Same setup, but with the layer threshold set to 2.
+		# Since the tip MIDX layer meets that threshold, its
+		# packs are considered repack candidates.
+		create_geometric_packs &&
+		cp $midx_chain $midx_chain.before &&
+
+		# Perturb the existing progression such that it is
+		# rolled up into a single new pack, invalidating the
+		# existing MIDX layer and replacing it with a new one.
+		test_commit extra &&
+		git repack -d &&
+		git repack --geometric=2 -d --write-midx=incremental \
+			--write-bitmap-index &&
+
+		! test_cmp $midx_chain.before $midx_chain &&
+		test_line_count = 1 $midx_chain &&
+
+		git multi-pack-index verify
+	)
+'
+
+test_expect_success 'above layer threshold, tip layer preserved' '
+	git init above-layer-threshold-tip-layer-preserved &&
+	(
+		cd above-layer-threshold-tip-layer-preserved &&
+
+		git config maintenance.auto false &&
+		git config repack.midxnewlayerthreshold 2 &&
+		git config repack.midxsplitfactor 2 &&
+
+		test_commit_bulk --message="medium" 2 &&
+		test_commit_bulk --message="large" 6 &&
+
+		git repack --geometric=2 -d --write-midx=incremental \
+			--write-bitmap-index &&
+
+		test_line_count = 1 "$midx_chain" &&
+		ls $packdir/pack-*.idx | sort >packs.before &&
+		cp $midx_chain $midx_chain.before &&
+
+		# Create objects to form a pack satisfying the geometric
+		# progression (thus preserving the tip layer), but not
+		# so large that it meets the layer merging condition.
+		test_commit_bulk --message="small" 1 &&
+		git repack --geometric=2 -d --write-midx=incremental \
+			--write-bitmap-index &&
+
+		ls $packdir/pack-*.idx | sort >packs.after &&
+		comm -13 packs.before packs.after >packs.new &&
+
+		test_line_count = 1 packs.new &&
+		test_line_count = 3 packs.after &&
+		test_line_count = 2 "$midx_chain" &&
+		head -n 1 "$midx_chain.before" >expect &&
+		head -n 1 "$midx_chain" >actual &&
+		test_cmp expect actual &&
+
+		git multi-pack-index verify
+	)
+'
+
+test_expect_success 'above layer threshold, tip packs preserved' '
+	git init above-layer-threshold-tip-packs-preserved &&
+	(
+		cd above-layer-threshold-tip-packs-preserved &&
+
+		git config maintenance.auto false &&
+		git config repack.midxnewlayerthreshold 2 &&
+		git config repack.midxsplitfactor 2 &&
+
+		create_geometric_packs &&
+		ls $packdir/pack-*.idx | sort >packs.before &&
+		cp $midx_chain $midx_chain.before &&
+
+		# Same setup as above, but this time the new objects do
+		# not satisfy the new layer merging condition, resulting
+		# in a new tip layer.
+		test_commit_bulk --message="huge" 18 &&
+		git repack --geometric=2 -d --write-midx=incremental \
+			--write-bitmap-index &&
+
+		ls $packdir/pack-*.idx | sort >packs.after &&
+		comm -13 packs.before packs.after >packs.new &&
+
+		! test_cmp $midx_chain.before $midx_chain &&
+		test_line_count = 1 $midx_chain &&
+		test_line_count = 1 packs.new &&
+
+		git multi-pack-index verify
+	)
+'
+
+test_expect_success 'new tip absorbs multiple layers' '
+	git init new-tip-absorbs-multiple-layers &&
+	(
+		cd new-tip-absorbs-multiple-layers &&
+
+		git config maintenance.auto false &&
+		git config repack.midxnewlayerthreshold 1 &&
+		git config repack.midxsplitfactor 2 &&
+
+		# Build a 4-layer chain where each layer is too small to
+		# absorb the one below it. The sizes must satisfy L(n) <
+		# L(n-1)/2 for each adjacent pair:
+		#
+		#   L0 (oldest): 75 obj (25 commits)
+		#   L1:          21 obj  (7 commits, 21 < 75/2)
+		#   L2:           9 obj  (3 commits,  9 < 21/2)
+		#   L3 (tip):     3 obj  (1 commit,   3 <  9/2)
+		create_layers <<-\EOF &&
+		L0 25
+		L1 7
+		L2 3
+		L3 1
+		EOF
+
+		test_line_count = 4 "$midx_chain" &&
+		cp $midx_chain $midx_chain.before &&
+
+		# Now add a new commit. The merging condition is
+		# satisfied between L3-L1, but violated at L0, which is
+		# too large relative to the accumulated size.
+		#
+		# As a result, the chain shrinks from 4 to 2 layers.
+		test_commit new &&
+		git repack --geometric=2 -d --write-midx=incremental \
+			--write-bitmap-index &&
+
+		! test_cmp $midx_chain.before $midx_chain &&
+		test_line_count = 2 "$midx_chain" &&
+		git multi-pack-index verify
+	)
+'
+
+test_expect_success 'compaction of older layers' '
+	git init compaction-of-older-layers &&
+	(
+		cd compaction-of-older-layers &&
+
+		git config maintenance.auto false &&
+		git config repack.midxnewlayerthreshold 1 &&
+		git config repack.midxsplitfactor 2 &&
+
+		# Build a chain with two small layers at the bottom
+		# and a larger barrier layer on top, producing a
+		# chain that violates the compaction invariant, since
+		# the two small layers would normally have been merged.
+		create_layers <<-\EOF &&
+		one 2
+		two 4
+		barrier 54
+		EOF
+
+		cp $midx_chain $midx_chain.before &&
+
+		# Running an incremental repack compacts the two
+		# small layers at the bottom of the chain as a
+		# separate step in the compaction plan.
+		test_commit another &&
+		git repack --geometric=2 -d --write-midx=incremental \
+			--write-bitmap-index &&
+
+		test_line_count = 2 "$midx_chain" &&
+		git multi-pack-index verify
+	)
+'
+
+test_expect_success 'geometric rollup with surviving tip packs' '
+	git init geometric-rollup-with-surviving-tip-packs &&
+	(
+		cd geometric-rollup-with-surviving-tip-packs &&
+
+		git config maintenance.auto false &&
+		git config repack.midxnewlayerthreshold 1 &&
+		git config repack.midxsplitfactor 2 &&
+
+		# Create a pack large enough to anchor the geometric
+		# progression when small packs are added alongside it.
+		create_layer --message="big" 5 &&
+
+		test_line_count = 1 "$midx_chain" &&
+		cp $midx_chain $midx_chain.before &&
+
+		# Repack a small number of objects such that the
+		# progression is unbothered. Note that the existing pack
+		# is considered a repack candidate as the new layer
+		# threshold is set to 1.
+		test_commit small-1 &&
+		git repack -d &&
+		git repack --geometric=2 -d --write-midx=incremental \
+			--write-bitmap-index &&
+
+		! test_cmp $midx_chain.before $midx_chain &&
+		cp $midx_chain $midx_chain.before
+	)
+'
+
+test_expect_success 'kept packs are excluded from repack' '
+	git init kept-packs-excluded-from-repack &&
+	(
+		cd kept-packs-excluded-from-repack &&
+
+		git config maintenance.auto false &&
+		git config repack.midxnewlayerthreshold 1 &&
+		git config repack.midxsplitfactor 2 &&
+
+		# Create two equal-sized packs, marking one as kept.
+		for i in A B
+		do
+			test_commit "$i" && git repack -d || return 1
+		done &&
+
+		keep=$(ls $packdir/pack-*.idx | head -n 1) &&
+		touch "${keep%.idx}.keep" &&
+
+		# The kept pack is excluded as a repacking candidate
+		# entirely, so no rollup occurs as there is only one
+		# non-kept pack. A new MIDX layer is written containing
+		# that pack.
+		git repack --geometric=2 -d --write-midx=incremental &&
+
+		test-tool read-midx $objdir >actual &&
+		grep "^pack-.*\.idx$" actual >actual.packs &&
+		test_line_count = 1 actual.packs &&
+		test_grep ! "$keep" actual.packs &&
+
+		git multi-pack-index verify &&
+
+		# All objects (from both kept and non-kept packs)
+		# must still be accessible.
+		git fsck
+	)
+'
+
+test_expect_success 'incremental MIDX with --max-pack-size' '
+	git init incremental-midx-with--max-pack-size &&
+	(
+		cd incremental-midx-with--max-pack-size &&
+
+		git config maintenance.auto false &&
+		git config repack.midxnewlayerthreshold 1 &&
+		git config repack.midxsplitfactor 2 &&
+
+		create_layer --message="base" 1 &&
+
+		# Now add enough data that a small --max-pack-size will
+		# cause pack-objects to split its output. Create objects
+		# large enough to fill multiple packs.
+		test-tool genrandom foo 1M >big1 &&
+		test-tool genrandom bar 1M >big2 &&
+		git add big1 big2 &&
+		test_tick &&
+		git commit -a -m "big blobs" &&
+		git repack -d &&
+
+		git repack --geometric=2 -d --write-midx=incremental \
+			--write-bitmap-index --max-pack-size=1M &&
+
+		test_line_count = 1 "$midx_chain" &&
+		test-tool read-midx $objdir >actual &&
+		grep "^pack-.*\.idx$" actual >actual.packs &&
+		test_line_count -gt 1 actual.packs &&
+
+		git multi-pack-index verify
+	)
+'
+
+test_expect_success 'noop repack preserves valid MIDX chain' '
+	git init noop-repack-preserves-valid-midx-chain &&
+	(
+		cd noop-repack-preserves-valid-midx-chain &&
+
+		git config maintenance.auto false &&
+		git config repack.midxnewlayerthreshold 1 &&
+		git config repack.midxsplitfactor 2 &&
+
+		create_layer --message="base" 1 &&
+
+		git multi-pack-index verify &&
+		cp $midx_chain $midx_chain.before &&
+
+		# Running again with no new objects should not break
+		# the MIDX chain. It produces "Nothing new to pack."
+		git repack --geometric=2 -d --write-midx=incremental \
+			--write-bitmap-index &&
+
+		test_cmp $midx_chain.before $midx_chain &&
+
+		git multi-pack-index verify &&
+		git fsck
+	)
+'
+
+test_expect_success 'repack -ad removes stale incremental chain' '
+	git init repack--ad-removes-stale-incremental-chain &&
+	(
+		cd repack--ad-removes-stale-incremental-chain &&
+
+		git config maintenance.auto false &&
+		git config repack.midxnewlayerthreshold 1 &&
+		git config repack.midxsplitfactor 2 &&
+
+		create_layers <<-\EOF &&
+		one 1
+		two 1
+		EOF
+
+		test_path_is_file $midx_chain &&
+		test_line_count = 2 $midx_chain &&
+
+		git repack -ad &&
+
+		test_path_is_missing $packdir/multi-pack-index &&
+		test_dir_is_empty $midxdir
+	)
+'
+
+test_expect_success 'repack -ad --write-midx=incremental is safe' '
+	git init ad-incremental-midx &&
+	(
+		cd ad-incremental-midx &&
+
+		git config maintenance.auto false &&
+
+		# Build a MIDX chain with multiple layers referencing
+		# distinct packs.
+		test_commit first &&
+		git repack -d &&
+
+		test_commit second &&
+		git repack -d --write-midx=incremental &&
+
+		git multi-pack-index verify &&
+		test_line_count = 1 $midx_chain &&
+
+		# Now do a full -ad repack. The new pack contains all
+		# objects, but any retained MIDX layers still reference
+		# the now-deleted packs.
+		test_commit third &&
+		git repack -ad --write-midx=incremental &&
+
+		git multi-pack-index verify &&
+		git fsck &&
+		git rev-list --all --objects >/dev/null
+	)
+'
+
+test_expect_success 'repack rejects invalid midxSplitFactor' '
+	test_when_finished "rm -fr bad-split-factor" &&
+	git init bad-split-factor &&
+	(
+		cd bad-split-factor &&
+		test_commit base &&
+
+		for v in 0 1 -1
+		do
+			test_must_fail git -c repack.midxSplitFactor=$v \
+				repack -d --geometric=2 --write-midx=incremental 2>err &&
+			test_grep "invalid value for --midx-split-factor" err ||
+			return 1
+		done
+	)
+'
+
+test_expect_success 'repack rejects invalid midxNewLayerThreshold' '
+	test_when_finished "rm -fr bad-layer-threshold" &&
+	git init bad-layer-threshold &&
+	(
+		cd bad-layer-threshold &&
+		test_commit base &&
+
+		for v in 0 -1
+		do
+			test_must_fail git -c repack.midxNewLayerThreshold=$v \
+				repack -d --geometric=2 --write-midx=incremental 2>err &&
+			test_grep "invalid value for --midx-new-layer-threshold" err ||
+			return 1
+		done
+	)
+'
+
+test_done
diff --git a/t/t7810-grep.sh b/t/t7810-grep.sh
index 64ac4f0..1b195be 100755
--- a/t/t7810-grep.sh
+++ b/t/t7810-grep.sh
@@ -322,11 +322,11 @@
 		${HC}file:1:5:mmap
 		${HC}file:2:5:mmap
 		${HC}file:3:5:mmap
-		${HC}file:3:13:mmap
+		${HC}file:3:14:mmap
 		${HC}file:4:5:mmap
-		${HC}file:4:13:mmap
+		${HC}file:4:14:mmap
 		${HC}file:5:5:mmap
-		${HC}file:5:13:mmap
+		${HC}file:5:14:mmap
 		EOF
 		git grep --column -n -o -e mmap $H >actual &&
 		test_cmp expected actual
@@ -1929,4 +1929,62 @@
 	test_cmp expected actual
 '
 
+test_expect_success 'grep of revision in partial clone batches prefetch and honors pathspec' '
+	test_when_finished "rm -rf grep-partial-src grep-partial" &&
+
+	git init grep-partial-src &&
+	(
+		cd grep-partial-src &&
+		git config uploadpack.allowfilter 1 &&
+		git config uploadpack.allowanysha1inwant 1 &&
+		mkdir a b &&
+		echo "needle in haystack" >a/matches.txt &&
+		echo "nothing to see here" >a/nomatch.txt &&
+		echo "needle again" >b/matches.md &&
+		git add . &&
+		git commit -m "initial"
+	) &&
+
+	git clone --no-checkout --filter=blob:none \
+		"file://$(pwd)/grep-partial-src" grep-partial &&
+
+	# All three blobs are missing immediately after a blobless clone.
+	git -C grep-partial rev-list --quiet --objects \
+		--missing=print HEAD >missing &&
+	test_line_count = 3 missing &&
+
+	# A pathspec-limited grep should prefetch only the two blobs
+	# in a/.  It should fetch both blobs in one batched request.
+	GIT_TRACE2_EVENT="$(pwd)/grep-trace-pathspec" \
+		git -C grep-partial grep -c "needle" HEAD -- "a/*.txt" >result &&
+
+	# Only a/matches.txt contains "needle" among the matched paths.
+	test_line_count = 1 result &&
+
+	# Exactly the two a/*.txt blobs should have been requested, and
+	# the server packed those two objects in the response.
+	test_trace2_data promisor fetch_count 2 <grep-trace-pathspec &&
+	test_trace2_data pack-objects written 2 <grep-trace-pathspec &&
+
+	# b/matches.md should still be missing locally.
+	git -C grep-partial rev-list --quiet --objects \
+		--missing=print HEAD >missing &&
+	test_line_count = 1 missing &&
+
+	# A second grep without a pathspec must recurse into both
+	# subdirectories, but should request only the still-missing blob
+	# from the promisor.
+	GIT_TRACE2_EVENT="$(pwd)/grep-trace-all" \
+		git -C grep-partial grep -c "needle" HEAD >result &&
+
+	test_line_count = 2 result &&
+	test_trace2_data promisor fetch_count 1 <grep-trace-all &&
+	test_trace2_data pack-objects written 1 <grep-trace-all &&
+
+	# Everything is local now.
+	git -C grep-partial rev-list --quiet --objects \
+		--missing=print HEAD >missing &&
+	test_line_count = 0 missing
+'
+
 test_done
diff --git a/t/t7900-maintenance.sh b/t/t7900-maintenance.sh
index 4700bea..d7f82e1 100755
--- a/t/t7900-maintenance.sh
+++ b/t/t7900-maintenance.sh
@@ -73,6 +73,31 @@
 	test_subcommand ! git maintenance run --auto --quiet --detach <false
 '
 
+test_expect_success 'gc.auto config option' '
+	GIT_TRACE2_EVENT="$(pwd)/default" git commit --quiet --allow-empty -m 1 &&
+	test_subcommand git maintenance run --auto --quiet --detach <default &&
+	GIT_TRACE2_EVENT="$(pwd)/true" \
+		git -c gc.auto=1 commit --quiet --allow-empty -m 2 &&
+	test_subcommand git maintenance run --auto --quiet --detach <true &&
+	GIT_TRACE2_EVENT="$(pwd)/false" \
+		git -c gc.auto=0 commit --quiet --allow-empty -m 3 &&
+	test_subcommand ! git maintenance run --auto --quiet --detach <false
+'
+
+test_expect_success 'maintenance.auto overrides gc.auto' '
+	test_when_finished "rm -f trace" &&
+
+	test_config maintenance.auto false &&
+	test_config gc.auto 1 &&
+	GIT_TRACE2_EVENT="$(pwd)/trace" git commit --quiet --allow-empty -m 1 &&
+	test_subcommand ! git maintenance run --auto --quiet --detach <trace &&
+
+	test_config maintenance.auto true &&
+	test_config gc.auto 0 &&
+	GIT_TRACE2_EVENT="$(pwd)/trace" git commit --quiet --allow-empty -m 1 &&
+	test_subcommand git maintenance run --auto --quiet --detach <trace
+'
+
 for cfg in maintenance.autoDetach gc.autoDetach
 do
 	test_expect_success "$cfg=true config option" '
@@ -101,8 +126,12 @@
 test_expect_success 'register uses XDG_CONFIG_HOME config if it exists' '
 	test_when_finished rm -r .config/git/config &&
 	(
+		# Override HOME so that .gitconfig (which test-lib.sh may
+		# have created, e.g. to set safe.bareRepository) does not
+		# take precedence over the XDG location.
+		HOME=$PWD/must-not-exist &&
 		XDG_CONFIG_HOME=.config &&
-		export XDG_CONFIG_HOME &&
+		export HOME XDG_CONFIG_HOME &&
 		mkdir -p $XDG_CONFIG_HOME/git &&
 		>$XDG_CONFIG_HOME/git/config &&
 		git maintenance register &&
@@ -124,8 +153,12 @@
 test_expect_success 'unregister uses XDG_CONFIG_HOME config if it exists' '
 	test_when_finished rm -r .config/git/config &&
 	(
+		# Override HOME so that .gitconfig (which test-lib.sh may
+		# have created, e.g. to set safe.bareRepository) does not
+		# take precedence over the XDG location.
+		HOME=$PWD/must-not-exist &&
 		XDG_CONFIG_HOME=.config &&
-		export XDG_CONFIG_HOME &&
+		export HOME XDG_CONFIG_HOME &&
 		mkdir -p $XDG_CONFIG_HOME/git &&
 		>$XDG_CONFIG_HOME/git/config &&
 		git maintenance register &&
@@ -532,7 +565,16 @@
 
 	# And verify that there are no loose objects anymore.
 	git count-objects -v >count &&
-	test_grep '^count: 0$' count
+	test_grep '^count: 0$' count &&
+
+	# Verify that no orphaned .idx files were left behind. On
+	# Windows, a missing odb_to_close causes the parent to hold
+	# mmap handles on .idx files, silently preventing their
+	# deletion by the child git-repack process.
+	ls .git/objects/pack/pack-*.idx .git/objects/pack/pack-*.pack |
+	sed "s/\.pack$/.idx/" |
+	sort | uniq -u >orphaned-idx &&
+	test_must_be_empty orphaned-idx
 }
 
 test_expect_success 'geometric repacking task' '
@@ -580,8 +622,19 @@
 
 		# And these two small packs should now be merged via the
 		# geometric repack. The large packfile should remain intact.
+		cp -R .git/objects .git/objects.save &&
 		run_and_verify_geometric_pack 2 &&
 
+		# On Windows, verify the same with legacy delete semantics
+		# that reject deletion of mmap-held .idx files.
+		if test_have_prereq MINGW
+		then
+			rm -rf .git/objects &&
+			mv .git/objects.save .git/objects &&
+			test_env GIT_TEST_LEGACY_DELETE=1 \
+				run_and_verify_geometric_pack 2
+		fi &&
+
 		# If we now add two more objects and repack twice we should
 		# then see another all-into-one repack. This time around
 		# though, as we have unreachable objects, we should also see a
@@ -1438,6 +1491,64 @@
 	)
 '
 
+test_expect_success PIPE '--detach holds maintenance lock until daemonized child exits' '
+	test_when_finished "rm -rf repo" &&
+	git init repo &&
+	(
+		cd repo &&
+
+		git config maintenance.auto false &&
+		git config core.lockfilepid true &&
+
+		git remote add origin /does/not/exist &&
+		git config set remote.origin.uploadpack "cat fifo-uploadpack" &&
+
+		mkfifo fifo-uploadpack fifo-maint &&
+
+		# Open the maintenance FIFO, as otherwise spawning
+		# git-maintenance(1) would block. Note that we need to open it
+		# as read-write, as otherwise we would block here already.
+		exec 9<>fifo-maint &&
+
+		{ git maintenance run --task=prefetch --detach 7>&9 & } &&
+		parent="$!" &&
+
+		# Reap the parent process so that the exec call below will not
+		# get SIGCHLD.
+		wait "$parent" &&
+
+		# Open the git-upload-pack(1) FIFO for writing, which will
+		# block until the upload-pack script opens it for reading. Once
+		# exec returns, we know that the daemonized child is alive and
+		# pinned.
+		exec 8>fifo-uploadpack &&
+
+		test_path_is_file .git/objects/maintenance.lock &&
+		test_path_is_file .git/objects/"maintenance~pid.lock" &&
+
+		# Verify that the maintenance.lock still exists, and
+		# that it was created by the parent process, not the
+		# child.
+		echo "pid $parent" >expect &&
+		test_cmp expect .git/objects/"maintenance~pid.lock" &&
+
+		# Reopen the maintenance FIFO as read-only so that
+		# git-maintenance(1) is the only writer. This will cause it to
+		# close the FIFO once the process exits.
+		exec 9<&- &&
+		exec 9<fifo-maint &&
+
+		# Close the FIFO used by git-upload-pack(1) to unblock it and
+		# then wait until the maintenance FIFO is closed by
+		# git-maintenance(1), indicating that it has exited.
+		exec 8>&- &&
+		cat <&9 &&
+
+		test_path_is_missing .git/objects/maintenance.lock &&
+		test_path_is_missing .git/objects/"maintenance~pid.lock"
+	)
+'
+
 test_expect_success '--detach causes maintenance to run in background' '
 	test_when_finished "rm -rf repo" &&
 	git init repo &&
diff --git a/t/t9138-git-svn-authors-prog.sh b/t/t9138-git-svn-authors-prog.sh
index 784ec7f..5bb38cb 100755
--- a/t/t9138-git-svn-authors-prog.sh
+++ b/t/t9138-git-svn-authors-prog.sh
@@ -68,8 +68,8 @@
 	)
 '
 
-git --git-dir=x/.git config --unset svn.authorsfile
-git --git-dir=x/.git config --unset svn.authorsprog
+git --git-dir=x/.git config --unset svn.authorsfile || :
+git --git-dir=x/.git config --unset svn.authorsprog || :
 
 test_expect_success 'authors-prog imported user without email' '
 	svn mkdir -m gg --username gg-hermit "$svnrepo"/gg &&
diff --git a/t/t9200-git-cvsexportcommit.sh b/t/t9200-git-cvsexportcommit.sh
index 14cbe96..581cf3d 100755
--- a/t/t9200-git-cvsexportcommit.sh
+++ b/t/t9200-git-cvsexportcommit.sh
@@ -11,8 +11,7 @@
 	test_done
 fi
 
-cvs >/dev/null 2>&1
-if test $? -ne 1
+if ! cvs version >/dev/null 2>&1
 then
     skip_all='skipping git cvsexportcommit tests, cvs not found'
     test_done
diff --git a/t/t9210-scalar.sh b/t/t9210-scalar.sh
index 009437a..f2a6df7 100755
--- a/t/t9210-scalar.sh
+++ b/t/t9210-scalar.sh
@@ -152,6 +152,10 @@
 '
 
 test_expect_success 'scalar clone' '
+	# index.skipHash (Scalar default) and GIT_TEST_SPLIT_INDEX are
+	# incompatible: the shared index gets a null OID and fails to
+	# load on re-read.
+	sane_unset GIT_TEST_SPLIT_INDEX &&
 	second=$(git rev-parse --verify second:second.t) &&
 	scalar clone "file://$(pwd)" cloned --single-branch &&
 	(
@@ -182,6 +186,7 @@
 '
 
 test_expect_success 'scalar clone --no-... opts' '
+	sane_unset GIT_TEST_SPLIT_INDEX &&
 	# Note: redirect stderr always to avoid having a verbose test
 	# run result in a difference in the --[no-]progress option.
 	GIT_TRACE2_EVENT="$(pwd)/no-opt-trace" scalar clone \
@@ -307,6 +312,7 @@
 
 SQ="'"
 test_expect_success UNZIP 'scalar diagnose' '
+	sane_unset GIT_TEST_SPLIT_INDEX &&
 	scalar clone "file://$(pwd)" cloned --single-branch &&
 	git repack &&
 	echo "$(pwd)/.git/objects/" >>cloned/src/.git/objects/info/alternates &&
diff --git a/t/t9211-scalar-clone.sh b/t/t9211-scalar-clone.sh
index bfbf22a..2043f48 100755
--- a/t/t9211-scalar-clone.sh
+++ b/t/t9211-scalar-clone.sh
@@ -8,6 +8,11 @@
 GIT_TEST_MAINT_SCHEDULER="crontab:test-tool crontab cron.txt,launchctl:true,schtasks:true"
 export GIT_TEST_MAINT_SCHEDULER
 
+# index.skipHash (Scalar default) and GIT_TEST_SPLIT_INDEX are
+# incompatible: the shared index gets a null OID and fails to
+# load on re-read.  Every test here uses scalar clone.
+sane_unset GIT_TEST_SPLIT_INDEX
+
 test_expect_success 'set up repository to clone' '
 	rm -rf .git &&
 	git init to-clone &&
diff --git a/t/t9305-fast-import-signatures.sh b/t/t9305-fast-import-signatures.sh
index 18707b3..5667693 100755
--- a/t/t9305-fast-import-signatures.sh
+++ b/t/t9305-fast-import-signatures.sh
@@ -103,7 +103,7 @@
 	test_line_count = 2 out
 '
 
-for mode in strip-if-invalid sign-if-invalid
+for mode in strip-if-invalid sign-if-invalid abort-if-invalid
 do
 	test_expect_success GPG "import commit with no signature with --signed-commits=$mode" '
 		git fast-export main >output &&
@@ -135,6 +135,14 @@
 		# corresponding `data <length>` command would have to be changed too.
 		sed "s/OpenPGP signed commit/OpenPGP forged commit/" output >modified &&
 
+		if test "$mode" = abort-if-invalid
+		then
+			test_must_fail git -C new fast-import --quiet \
+				--signed-commits=$mode <modified >log 2>&1 &&
+			test_grep "aborting due to invalid signature" log &&
+			return 0
+		fi &&
+
 		git -C new fast-import --quiet --signed-commits=$mode <modified >log 2>&1 &&
 
 		IMPORTED=$(git -C new rev-parse --verify refs/heads/openpgp-signing) &&
diff --git a/t/t9306-fast-import-signed-tags.sh b/t/t9306-fast-import-signed-tags.sh
index 363619e..ec2b241 100755
--- a/t/t9306-fast-import-signed-tags.sh
+++ b/t/t9306-fast-import-signed-tags.sh
@@ -77,4 +77,122 @@
 	test_grep ! "SSH SIGNATURE" out
 '
 
+for mode in strip-if-invalid sign-if-invalid abort-if-invalid
+do
+	test_expect_success GPG "import tag with no signature with --signed-tags=$mode" '
+		test_when_finished rm -rf import &&
+		git init import &&
+
+		git fast-export --signed-tags=verbatim >output &&
+		git -C import fast-import --quiet --signed-tags=$mode <output >log 2>&1 &&
+		test_must_be_empty log
+	'
+
+	test_expect_success GPG "keep valid OpenPGP signature with --signed-tags=$mode" '
+		test_when_finished rm -rf import &&
+		git init import &&
+
+		git fast-export --signed-tags=verbatim openpgp-signed >output &&
+		git -C import fast-import --quiet --signed-tags=$mode <output >log 2>&1 &&
+		IMPORTED=$(git -C import rev-parse --verify refs/tags/openpgp-signed) &&
+		test $OPENPGP_SIGNED = $IMPORTED &&
+		git -C import cat-file tag "$IMPORTED" >actual &&
+		test_grep -E "^-----BEGIN PGP SIGNATURE-----" actual &&
+		test_must_be_empty log
+	'
+
+	test_expect_success GPG "handle signature invalidated by message change with --signed-tags=$mode" '
+		test_when_finished rm -rf import &&
+		git init import &&
+
+		git fast-export --signed-tags=verbatim openpgp-signed >output &&
+
+		# Change the tag message, which invalidates the signature. The tag
+		# message length should not change though, otherwise the corresponding
+		# `data <length>` command would have to be changed too.
+		sed "s/OpenPGP signed tag/OpenPGP forged tag/" output >modified &&
+
+		if test "$mode" = abort-if-invalid
+		then
+			test_must_fail git -C import fast-import --quiet \
+				--signed-tags=$mode <modified >log 2>&1 &&
+			test_grep "aborting due to invalid signature" log &&
+			return 0
+		fi &&
+
+		git -C import fast-import --quiet --signed-tags=$mode <modified >log 2>&1 &&
+
+		IMPORTED=$(git -C import rev-parse --verify refs/tags/openpgp-signed) &&
+		test $OPENPGP_SIGNED != $IMPORTED &&
+		git -C import cat-file tag "$IMPORTED" >actual &&
+
+		if test "$mode" = strip-if-invalid
+		then
+			test_grep ! -E "^-----BEGIN PGP SIGNATURE-----" actual
+		else
+			test_grep -E "^-----BEGIN PGP SIGNATURE-----" actual &&
+			git -C import verify-tag "$IMPORTED"
+		fi &&
+
+		test_must_be_empty log
+	'
+
+	test_expect_success GPGSM "keep valid X.509 signature with --signed-tags=$mode" '
+		test_when_finished rm -rf import &&
+		git init import &&
+
+		git fast-export --signed-tags=verbatim x509-signed >output &&
+		git -C import fast-import --quiet --signed-tags=$mode <output >log 2>&1 &&
+		IMPORTED=$(git -C import rev-parse --verify refs/tags/x509-signed) &&
+		test $X509_SIGNED = $IMPORTED &&
+		git -C import cat-file tag x509-signed >actual &&
+		test_grep -E "^-----BEGIN SIGNED MESSAGE-----" actual &&
+		test_must_be_empty log
+	'
+
+	test_expect_success GPGSSH "keep valid SSH signature with --signed-tags=$mode" '
+		test_when_finished rm -rf import &&
+		git init import &&
+
+		test_config -C import gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
+
+		git fast-export --signed-tags=verbatim ssh-signed >output &&
+		git -C import fast-import --quiet --signed-tags=$mode <output >log 2>&1 &&
+		IMPORTED=$(git -C import rev-parse --verify refs/tags/ssh-signed) &&
+		test $SSH_SIGNED = $IMPORTED &&
+		git -C import cat-file tag ssh-signed >actual &&
+		test_grep -E "^-----BEGIN SSH SIGNATURE-----" actual &&
+		test_must_be_empty log
+	'
+done
+
+test_expect_success GPGSSH 'sign invalid tag with explicit keyid' '
+	test_when_finished rm -rf import &&
+	git init import &&
+
+	git fast-export --signed-tags=verbatim ssh-signed >output &&
+
+	# Change the tag message, which invalidates the signature. The tag
+	# message length should not change though, otherwise the corresponding
+	# `data <length>` command would have to be changed too.
+	sed "s/SSH signed tag/SSH forged tag/" output >modified &&
+
+	# Configure the target repository with an invalid default signing key.
+	test_config -C import user.signingkey "not-a-real-key-id" &&
+	test_config -C import gpg.format ssh &&
+	test_config -C import gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
+	test_must_fail git -C import fast-import --quiet \
+		--signed-tags=sign-if-invalid <modified >/dev/null 2>&1 &&
+
+	# Import using explicitly provided signing key.
+	git -C import fast-import --quiet \
+		--signed-tags=sign-if-invalid="${GPGSSH_KEY_PRIMARY}" <modified &&
+
+	IMPORTED=$(git -C import rev-parse --verify refs/tags/ssh-signed) &&
+	test $SSH_SIGNED != $IMPORTED &&
+	git -C import cat-file tag "$IMPORTED" >actual &&
+	test_grep -E "^-----BEGIN SSH SIGNATURE-----" actual &&
+	git -C import verify-tag "$IMPORTED"
+'
+
 test_done
diff --git a/t/t9400-git-cvsserver-server.sh b/t/t9400-git-cvsserver-server.sh
index e499c7f..4b45398 100755
--- a/t/t9400-git-cvsserver-server.sh
+++ b/t/t9400-git-cvsserver-server.sh
@@ -17,12 +17,13 @@
 	skip_all='skipping git cvsserver tests, perl not available'
 	test_done
 fi
-cvs >/dev/null 2>&1
-if test $? -ne 1
+
+if ! cvs version >/dev/null 2>&1
 then
     skip_all='skipping git-cvsserver tests, cvs not found'
     test_done
 fi
+
 perl -e 'use DBI; use DBD::SQLite' >/dev/null 2>&1 || {
     skip_all='skipping git-cvsserver tests, Perl SQLite interface unavailable'
     test_done
diff --git a/t/t9401-git-cvsserver-crlf.sh b/t/t9401-git-cvsserver-crlf.sh
index a34805a..6b4cbb1 100755
--- a/t/t9401-git-cvsserver-crlf.sh
+++ b/t/t9401-git-cvsserver-crlf.sh
@@ -60,12 +60,12 @@
     return $stat
 }
 
-cvs >/dev/null 2>&1
-if test $? -ne 1
+if ! cvs version >/dev/null 2>&1
 then
     skip_all='skipping git-cvsserver tests, cvs not found'
     test_done
 fi
+
 if ! test_have_prereq PERL
 then
     skip_all='skipping git-cvsserver tests, perl not available'
diff --git a/t/t9402-git-cvsserver-refs.sh b/t/t9402-git-cvsserver-refs.sh
index 2ee41f9..65f2cee 100755
--- a/t/t9402-git-cvsserver-refs.sh
+++ b/t/t9402-git-cvsserver-refs.sh
@@ -68,12 +68,12 @@
 
 #########
 
-cvs >/dev/null 2>&1
-if test $? -ne 1
+if ! cvs version >/dev/null 2>&1
 then
 	skip_all='skipping git-cvsserver tests, cvs not found'
 	test_done
 fi
+
 if ! test_have_prereq PERL
 then
 	skip_all='skipping git-cvsserver tests, perl not available'
diff --git a/t/t9902-completion.sh b/t/t9902-completion.sh
index 2f9a597..28f61f0 100755
--- a/t/t9902-completion.sh
+++ b/t/t9902-completion.sh
@@ -590,12 +590,10 @@
 	__gitcomp "$invalid_variable_name"
 '
 
-read -r -d "" refs <<-\EOF
-main
+refs='main
 maint
 next
-seen
-EOF
+seen'
 
 test_expect_success '__gitcomp_nl - trailing space' '
 	test_gitcomp_nl "m" "$refs" <<-EOF
diff --git a/t/t9904-url-parse.sh b/t/t9904-url-parse.sh
new file mode 100755
index 0000000..8a369d2
--- /dev/null
+++ b/t/t9904-url-parse.sh
@@ -0,0 +1,319 @@
+#!/bin/sh
+#
+# Copyright (c) 2024 Matheus Afonso Martins Moreira
+#
+
+test_description='git url-parse tests'
+
+. ./test-lib.sh
+
+test_expect_success 'git url-parse -- ssh syntax' '
+	git url-parse "ssh://user@example.com:1234/repository/path" &&
+	git url-parse "ssh://user@example.com/repository/path" &&
+	git url-parse "ssh://example.com:1234/repository/path" &&
+	git url-parse "ssh://example.com/repository/path"
+'
+
+test_expect_success 'git url-parse -- git syntax' '
+	git url-parse "git://example.com:1234/repository/path" &&
+	git url-parse "git://example.com/repository/path"
+'
+
+test_expect_success 'git url-parse -- http syntax' '
+	git url-parse "https://example.com:1234/repository/path" &&
+	git url-parse "https://example.com/repository/path" &&
+	git url-parse "http://example.com:1234/repository/path" &&
+	git url-parse "http://example.com/repository/path"
+'
+
+test_expect_success 'git url-parse -- scp syntax' '
+	git url-parse "user@example.com:/repository/path" &&
+	git url-parse "example.com:/repository/path"
+'
+
+test_expect_success 'git url-parse -- username expansion - ssh syntax' '
+	git url-parse "ssh://user@example.com:1234/~user/repository" &&
+	git url-parse "ssh://user@example.com/~user/repository" &&
+	git url-parse "ssh://example.com:1234/~user/repository" &&
+	git url-parse "ssh://example.com/~user/repository"
+'
+
+test_expect_success 'git url-parse -- username expansion - git syntax' '
+	git url-parse "git://example.com:1234/~user/repository" &&
+	git url-parse "git://example.com/~user/repository"
+'
+
+test_expect_success 'git url-parse -- username expansion - scp syntax' '
+	git url-parse "user@example.com:~user/repository" &&
+	git url-parse "example.com:~user/repository"
+'
+
+test_expect_success 'git url-parse -- file urls' '
+	git url-parse "file:///repository/path" &&
+	git url-parse "file://"
+'
+
+test_expect_success 'git url-parse -c scheme -- ssh syntax' '
+	test ssh = "$(git url-parse -c scheme "ssh://user@example.com:1234/repository/path")" &&
+	test ssh = "$(git url-parse -c scheme "ssh://user@example.com/repository/path")" &&
+	test ssh = "$(git url-parse -c scheme "ssh://example.com:1234/repository/path")" &&
+	test ssh = "$(git url-parse -c scheme "ssh://example.com/repository/path")"
+'
+
+test_expect_success 'git url-parse -c scheme -- git syntax' '
+	test git = "$(git url-parse -c scheme "git://example.com:1234/repository/path")" &&
+	test git = "$(git url-parse -c scheme "git://example.com/repository/path")"
+'
+
+test_expect_success 'git url-parse -c scheme -- http syntax' '
+	test https = "$(git url-parse -c scheme "https://example.com:1234/repository/path")" &&
+	test https = "$(git url-parse -c scheme "https://example.com/repository/path")" &&
+	test http = "$(git url-parse -c scheme "http://example.com:1234/repository/path")" &&
+	test http = "$(git url-parse -c scheme "http://example.com/repository/path")"
+'
+
+test_expect_success 'git url-parse -c scheme -- scp syntax' '
+	test ssh = "$(git url-parse -c scheme "user@example.com:/repository/path")" &&
+	test ssh = "$(git url-parse -c scheme "example.com:/repository/path")"
+'
+
+test_expect_success 'git url-parse -c user -- ssh syntax' '
+	test user = "$(git url-parse -c user "ssh://user@example.com:1234/repository/path")" &&
+	test user = "$(git url-parse -c user "ssh://user@example.com/repository/path")" &&
+	test "" = "$(git url-parse -c user "ssh://example.com:1234/repository/path")" &&
+	test "" = "$(git url-parse -c user "ssh://example.com/repository/path")"
+'
+
+test_expect_success 'git url-parse -c user -- git syntax' '
+	test "" = "$(git url-parse -c user "git://example.com:1234/repository/path")" &&
+	test "" = "$(git url-parse -c user "git://example.com/repository/path")"
+'
+
+test_expect_success 'git url-parse -c user -- http syntax' '
+	test "" = "$(git url-parse -c user "https://example.com:1234/repository/path")" &&
+	test "" = "$(git url-parse -c user "https://example.com/repository/path")" &&
+	test "" = "$(git url-parse -c user "http://example.com:1234/repository/path")" &&
+	test "" = "$(git url-parse -c user "http://example.com/repository/path")"
+'
+
+test_expect_success 'git url-parse -c user -- scp syntax' '
+	test user = "$(git url-parse -c user "user@example.com:/repository/path")" &&
+	test "" = "$(git url-parse -c user "example.com:/repository/path")"
+'
+
+test_expect_success 'git url-parse -c password -- http syntax' '
+	test secret = "$(git url-parse -c password "https://user:secret@example.com:1234/repository/path")" &&
+	test secret = "$(git url-parse -c password "http://user:secret@example.com/repository/path")" &&
+	test "" = "$(git url-parse -c password "https://user@example.com/repository/path")" &&
+	test "" = "$(git url-parse -c password "https://example.com/repository/path")"
+'
+
+test_expect_success 'git url-parse -c host -- ssh syntax' '
+	test example.com = "$(git url-parse -c host "ssh://user@example.com:1234/repository/path")" &&
+	test example.com = "$(git url-parse -c host "ssh://user@example.com/repository/path")" &&
+	test example.com = "$(git url-parse -c host "ssh://example.com:1234/repository/path")" &&
+	test example.com = "$(git url-parse -c host "ssh://example.com/repository/path")"
+'
+
+test_expect_success 'git url-parse -c host -- git syntax' '
+	test example.com = "$(git url-parse -c host "git://example.com:1234/repository/path")" &&
+	test example.com = "$(git url-parse -c host "git://example.com/repository/path")"
+'
+
+test_expect_success 'git url-parse -c host -- http syntax' '
+	test example.com = "$(git url-parse -c host "https://example.com:1234/repository/path")" &&
+	test example.com = "$(git url-parse -c host "https://example.com/repository/path")" &&
+	test example.com = "$(git url-parse -c host "http://example.com:1234/repository/path")" &&
+	test example.com = "$(git url-parse -c host "http://example.com/repository/path")"
+'
+
+test_expect_success 'git url-parse -c host -- scp syntax' '
+	test example.com = "$(git url-parse -c host "user@example.com:/repository/path")" &&
+	test example.com = "$(git url-parse -c host "example.com:/repository/path")"
+'
+
+test_expect_success 'git url-parse -c port -- ssh syntax' '
+	test 1234 = "$(git url-parse -c port "ssh://user@example.com:1234/repository/path")" &&
+	test "" = "$(git url-parse -c port "ssh://user@example.com/repository/path")" &&
+	test 1234 = "$(git url-parse -c port "ssh://example.com:1234/repository/path")" &&
+	test "" = "$(git url-parse -c port "ssh://example.com/repository/path")"
+'
+
+test_expect_success 'git url-parse -c port -- git syntax' '
+	test 1234 = "$(git url-parse -c port "git://example.com:1234/repository/path")" &&
+	test "" = "$(git url-parse -c port "git://example.com/repository/path")"
+'
+
+test_expect_success 'git url-parse -c port -- http syntax' '
+	test 1234 = "$(git url-parse -c port "https://example.com:1234/repository/path")" &&
+	test "" = "$(git url-parse -c port "https://example.com/repository/path")" &&
+	test 1234 = "$(git url-parse -c port "http://example.com:1234/repository/path")" &&
+	test "" = "$(git url-parse -c port "http://example.com/repository/path")"
+'
+
+test_expect_success 'git url-parse -c port -- scp syntax' '
+	test "" = "$(git url-parse -c port "user@example.com:/repository/path")" &&
+	test "" = "$(git url-parse -c port "example.com:/repository/path")"
+'
+
+test_expect_success 'git url-parse -c path -- ssh syntax' '
+	test "/repository/path" = "$(git url-parse -c path "ssh://user@example.com:1234/repository/path")" &&
+	test "/repository/path" = "$(git url-parse -c path "ssh://user@example.com/repository/path")" &&
+	test "/repository/path" = "$(git url-parse -c path "ssh://example.com:1234/repository/path")" &&
+	test "/repository/path" = "$(git url-parse -c path "ssh://example.com/repository/path")"
+'
+
+test_expect_success 'git url-parse -c path -- git syntax' '
+	test "/repository/path" = "$(git url-parse -c path "git://example.com:1234/repository/path")" &&
+	test "/repository/path" = "$(git url-parse -c path "git://example.com/repository/path")"
+'
+
+test_expect_success 'git url-parse -c path -- http syntax' '
+	test "/repository/path" = "$(git url-parse -c path "https://example.com:1234/repository/path")" &&
+	test "/repository/path" = "$(git url-parse -c path "https://example.com/repository/path")" &&
+	test "/repository/path" = "$(git url-parse -c path "http://example.com:1234/repository/path")" &&
+	test "/repository/path" = "$(git url-parse -c path "http://example.com/repository/path")"
+'
+
+test_expect_success 'git url-parse -c path -- scp syntax' '
+	test "/repository/path" = "$(git url-parse -c path "user@example.com:/repository/path")" &&
+	test "/repository/path" = "$(git url-parse -c path "example.com:/repository/path")"
+'
+
+test_expect_success 'git url-parse -c path -- username expansion - ssh syntax' '
+	test "~user/repository" = "$(git url-parse -c path "ssh://user@example.com:1234/~user/repository")" &&
+	test "~user/repository" = "$(git url-parse -c path "ssh://user@example.com/~user/repository")" &&
+	test "~user/repository" = "$(git url-parse -c path "ssh://example.com:1234/~user/repository")" &&
+	test "~user/repository" = "$(git url-parse -c path "ssh://example.com/~user/repository")"
+'
+
+test_expect_success 'git url-parse -c path -- username expansion - git syntax' '
+	test "~user/repository" = "$(git url-parse -c path "git://example.com:1234/~user/repository")" &&
+	test "~user/repository" = "$(git url-parse -c path "git://example.com/~user/repository")"
+'
+
+test_expect_success 'git url-parse -c path -- username expansion - scp syntax' '
+	test "~user/repository" = "$(git url-parse -c path "user@example.com:~user/repository")" &&
+	test "~user/repository" = "$(git url-parse -c path "example.com:~user/repository")"
+'
+
+test_expect_success 'git url-parse -c path -- username expansion strips query and fragment' '
+	test "~user/repository" = "$(git url-parse -c path "ssh://example.com/~user/repository?query")" &&
+	test "~user/repository" = "$(git url-parse -c path "ssh://example.com/~user/repository#fragment")" &&
+	test "~user/repository" = "$(git url-parse -c path "git://example.com/~user/repository?query")" &&
+	test "~user/repository" = "$(git url-parse -c path "user@example.com:~user/repository?query")"
+'
+
+test_expect_success 'git url-parse -- ssh syntax with IPv6' '
+	git url-parse "ssh://user@[::1]:1234/repository/path" &&
+	git url-parse "ssh://user@[::1]/repository/path" &&
+	git url-parse "ssh://[::1]:1234/repository/path" &&
+	git url-parse "ssh://[::1]/repository/path" &&
+	git url-parse "ssh://[2001:db8::1]/repository/path"
+'
+
+test_expect_success 'git url-parse -- git syntax with IPv6' '
+	git url-parse "git://[::1]:9418/repository/path" &&
+	git url-parse "git://[::1]/repository/path"
+'
+
+test_expect_success 'git url-parse -- http syntax with IPv6' '
+	git url-parse "https://[::1]:1234/repository/path" &&
+	git url-parse "https://[::1]/repository/path" &&
+	git url-parse "http://[2001:db8::1]/repository/path"
+'
+
+test_expect_success 'git url-parse -c host -- IPv6 in URL form' '
+	test "[::1]" = "$(git url-parse -c host "ssh://user@[::1]:1234/repository/path")" &&
+	test "[::1]" = "$(git url-parse -c host "ssh://[::1]/repository/path")" &&
+	test "[2001:db8::1]" = "$(git url-parse -c host "ssh://[2001:db8::1]/repository/path")" &&
+	test "[::1]" = "$(git url-parse -c host "git://[::1]/repository/path")" &&
+	test "[2001:db8::1]" = "$(git url-parse -c host "https://[2001:db8::1]/repository/path")"
+'
+
+test_expect_success 'git url-parse -c port -- IPv6 in URL form' '
+	test 1234 = "$(git url-parse -c port "ssh://user@[::1]:1234/repository/path")" &&
+	test "" = "$(git url-parse -c port "ssh://[::1]/repository/path")" &&
+	test 9418 = "$(git url-parse -c port "git://[::1]:9418/repository/path")"
+'
+
+test_expect_success 'git url-parse -- scp syntax with IPv6' '
+	git url-parse "[::1]:repository/path" &&
+	git url-parse "user@[::1]:repository/path" &&
+	git url-parse "[2001:db8::1]:repo"
+'
+
+test_expect_success 'git url-parse -- scp syntax with bracketed hostname' '
+	git url-parse "[myhost]:src" &&
+	git url-parse "user@[myhost]:src"
+'
+
+test_expect_success 'git url-parse -- scp syntax with bracketed host:port' '
+	git url-parse "[myhost:123]:src" &&
+	git url-parse "user@[myhost:123]:src"
+'
+
+test_expect_success 'git url-parse -c host -- scp+IPv6' '
+	test "[::1]" = "$(git url-parse -c host "[::1]:repository/path")" &&
+	test "[::1]" = "$(git url-parse -c host "user@[::1]:repository/path")" &&
+	test "[2001:db8::1]" = "$(git url-parse -c host "[2001:db8::1]:repo")"
+'
+
+test_expect_success 'git url-parse -c path -- scp+IPv6' '
+	test "/repository/path" = "$(git url-parse -c path "[::1]:/repository/path")" &&
+	test "/repository/path" = "$(git url-parse -c path "[::1]:repository/path")" &&
+	test "/repo" = "$(git url-parse -c path "[2001:db8::1]:repo")"
+'
+
+test_expect_success 'git url-parse -c host,port,path -- scp [host:port]:src' '
+	test myhost = "$(git url-parse -c host "[myhost:123]:src")" &&
+	test 123 = "$(git url-parse -c port "[myhost:123]:src")" &&
+	test "/src" = "$(git url-parse -c path "[myhost:123]:src")"
+'
+
+test_expect_success 'git url-parse -c host,path -- scp [host]:src' '
+	test myhost = "$(git url-parse -c host "[myhost]:src")" &&
+	test "/src" = "$(git url-parse -c path "[myhost]:src")"
+'
+
+test_expect_success 'git url-parse -c user -- scp with user@ and brackets' '
+	test user = "$(git url-parse -c user "user@[::1]:repo")" &&
+	test user = "$(git url-parse -c user "user@[myhost:123]:src")" &&
+	test user = "$(git url-parse -c user "user@[myhost]:src")"
+'
+
+test_expect_success 'git url-parse -- scp+IPv6 with username expansion' '
+	test "~user/repo" = "$(git url-parse -c path "[::1]:~user/repo")" &&
+	test "~user/repo" = "$(git url-parse -c path "user@[::1]:~user/repo")"
+'
+
+test_expect_success 'git url-parse fails on invalid URL' '
+	test_must_fail git url-parse "not a url"
+'
+
+test_expect_success 'git url-parse helpful error for absolute local path' '
+	test_must_fail git url-parse "/abs/path" 2>err &&
+	test_grep "is not a URL" err &&
+	test_grep "file:///" err
+'
+
+test_expect_success 'git url-parse helpful error for relative local path' '
+	test_must_fail git url-parse "./rel" 2>err &&
+	test_grep "is not a URL" err &&
+	test_grep "absolute path" err
+'
+
+test_expect_success 'git url-parse fails on unknown -c component name' '
+	test_must_fail git url-parse -c bogus "https://example.com/repo"
+'
+
+test_expect_success 'git url-parse fails on URL missing host' '
+	test_must_fail git url-parse "https://"
+'
+
+test_expect_success 'git url-parse with no URL prints usage' '
+	test_must_fail git url-parse 2>err &&
+	test_grep "usage:" err
+'
+
+test_done
diff --git a/t/test-lib-functions.sh b/t/test-lib-functions.sh
index f3af10f..809c662 100644
--- a/t/test-lib-functions.sh
+++ b/t/test-lib-functions.sh
@@ -1195,8 +1195,9 @@
 		echo >&7 "test_must_fail: only 'git' is allowed: $*"
 		return 1
 	fi
-	"$@" 2>&7
-	exit_code=$?
+
+	exit_code=0; "$@" 2>&7 || exit_code=$?
+
 	if test $exit_code -eq 0 && ! list_contains "$_test_ok" success
 	then
 		echo >&4 "test_must_fail: command succeeded: $*"
@@ -1247,8 +1248,7 @@
 test_expect_code () {
 	want_code=$1
 	shift
-	"$@" 2>&7
-	exit_code=$?
+	exit_code=0; "$@" 2>&7 || exit_code=$?
 	if test $exit_code = $want_code
 	then
 		return 0
@@ -1512,7 +1512,7 @@
 	test "${BASH_SUBSHELL-0}" = 0 ||
 	BUG "test_when_finished does nothing in a subshell"
 	test_cleanup="{ $*
-		} && (exit \"\$eval_ret\"); eval_ret=\$?; $test_cleanup"
+		} || eval_ret=\$?; $test_cleanup"
 }
 
 # This function can be used to schedule some commands to be run
@@ -1540,7 +1540,7 @@
 	test "${BASH_SUBSHELL-0}" = 0 ||
 	BUG "test_atexit does nothing in a subshell"
 	test_atexit_cleanup="{ $*
-		} && (exit \"\$eval_ret\"); eval_ret=\$?; $test_atexit_cleanup"
+		} || eval_ret=\$?; $test_atexit_cleanup"
 }
 
 # Deprecated wrapper for "git init", use "git init" directly instead
@@ -2069,3 +2069,11 @@
 test_redact_non_printables () {
     tr -d "\n\r" | tr "[\001-\040][\177-\377]" "."
 }
+
+# Remove .gitconfig entries from a file in place.  test-lib.sh may
+# create $HOME/.gitconfig (e.g. to set safe.bareRepository) which
+# can appear in ls-files or status output.
+test_filter_gitconfig () {
+	sed "/\\.gitconfig/d" "$1" >"$1.filtered" &&
+	mv "$1.filtered" "$1"
+}
diff --git a/t/test-lib.sh b/t/test-lib.sh
index 70fd3e9..4a7357b 100644
--- a/t/test-lib.sh
+++ b/t/test-lib.sh
@@ -15,6 +15,31 @@
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see https://www.gnu.org/licenses/ .
 
+# Enable the use of errexit so that any unexpected failures will cause us to
+# abort tests, even when outside of a specific test case.
+#
+# Note that we only enable this on Bash 5 and newer, or when explicitly
+# requested by the user via `GIT_TEST_USE_SET_E=true`. This ib secause `set -e`
+# has wildly different behaviour across shells. The list of default-enabled
+# shells may be extended going forward.
+if test -z "$GIT_TEST_USE_SET_E" && test "${BASH_VERSINFO:=0}" -ge 5
+then
+	GIT_TEST_USE_SET_E=true
+fi
+
+# We cannot use `test-tool env-helper` here, as it's not yet available.
+case "${GIT_TEST_USE_SET_E:-false}" in
+1|on|true|yes)
+	set -e
+	;;
+0|off|false|no)
+	;;
+*)
+	echo "GIT_TEST_USE_SET_E requires a boolean" >&2
+	exit 1
+	;;
+esac
+
 # Test the binaries we have just built.  The tests are kept in
 # t/ subdirectory and are run in 'trash directory' subdirectory.
 if test -z "$TEST_DIRECTORY"
@@ -143,8 +168,8 @@
 ################################################################
 # It appears that people try to run tests without building...
 GIT_BINARY="${GIT_TEST_INSTALLED:-$GIT_BUILD_DIR}/git$X"
-"$GIT_BINARY" >/dev/null
-if test $? != 1
+
+if ! "$GIT_BINARY" version >/dev/null
 then
 	if test -n "$GIT_TEST_INSTALLED"
 	then
@@ -454,8 +479,10 @@
 	# from any previous runs.
 	>"$GIT_TEST_TEE_OUTPUT_FILE"
 
-	(GIT_TEST_TEE_STARTED=done ${TEST_SHELL_PATH} "$0" "$@" 2>&1;
-	 echo $? >"$TEST_RESULTS_BASE.exit") | tee -a "$GIT_TEST_TEE_OUTPUT_FILE"
+	(
+		ret=0 && GIT_TEST_TEE_STARTED=done ${TEST_SHELL_PATH} "$0" "$@" 2>&1 || ret=$?
+		echo "$ret" >"$TEST_RESULTS_BASE.exit"
+	) | tee -a "$GIT_TEST_TEE_OUTPUT_FILE"
 	test "$(cat "$TEST_RESULTS_BASE.exit")" = 0
 	exit
 fi
@@ -1597,6 +1624,19 @@
 TRASH_DIRECTORY=$(pwd)
 HOME="$TRASH_DIRECTORY"
 
+if test -n "$WITH_BREAKING_CHANGES"
+then
+	git config --global safe.bareRepository all &&
+	# Only write to .git/info/exclude when the directory exists
+	# (i.e. when git init created the repo). If we mkdir -p it
+	# ourselves, tests that expect to create .git/info/ themselves
+	# (e.g. t0008) would fail.
+	if test -d .git/info
+	then
+		echo "/.gitconfig" >>.git/info/exclude
+	fi
+fi
+
 start_test_output "$0"
 
 # Convenience
diff --git a/t/unit-tests/u-odb-inmemory.c b/t/unit-tests/u-odb-inmemory.c
new file mode 100644
index 0000000..482502e
--- /dev/null
+++ b/t/unit-tests/u-odb-inmemory.c
@@ -0,0 +1,313 @@
+#include "unit-test.h"
+#include "hex.h"
+#include "odb/source-inmemory.h"
+#include "odb/streaming.h"
+#include "oidset.h"
+#include "repository.h"
+#include "strbuf.h"
+
+#define RANDOM_OID "da39a3ee5e6b4b0d3255bfef95601890afd80709"
+#define FOOBAR_OID "f6ea0495187600e7b2288c8ac19c5886383a4632"
+
+static struct repository repo = {
+	.hash_algo = &hash_algos[GIT_HASH_SHA1],
+};
+static struct object_database *odb;
+
+static void cl_assert_object_info(struct odb_source_inmemory *source,
+				  const struct object_id *oid,
+				  enum object_type expected_type,
+				  const char *expected_content)
+{
+	enum object_type actual_type;
+	unsigned long actual_size;
+	void *actual_content;
+	struct object_info oi = {
+		.typep = &actual_type,
+		.sizep = &actual_size,
+		.contentp = &actual_content,
+	};
+
+	cl_must_pass(odb_source_read_object_info(&source->base, oid, &oi, 0));
+	cl_assert_equal_u(actual_size, strlen(expected_content));
+	cl_assert_equal_u(actual_type, expected_type);
+	cl_assert_equal_s((char *) actual_content, expected_content);
+
+	free(actual_content);
+}
+
+void test_odb_inmemory__initialize(void)
+{
+	odb = odb_new(&repo, "", "");
+}
+
+void test_odb_inmemory__cleanup(void)
+{
+	odb_free(odb);
+}
+
+void test_odb_inmemory__new(void)
+{
+	struct odb_source_inmemory *source = odb_source_inmemory_new(odb);
+	cl_assert_equal_i(source->base.type, ODB_SOURCE_INMEMORY);
+	odb_source_free(&source->base);
+}
+
+void test_odb_inmemory__read_missing_object(void)
+{
+	struct odb_source_inmemory *source = odb_source_inmemory_new(odb);
+	struct object_id oid;
+	const char *end;
+
+	cl_must_pass(parse_oid_hex_algop(RANDOM_OID, &oid, &end, repo.hash_algo));
+	cl_must_fail(odb_source_read_object_info(&source->base, &oid, NULL, 0));
+
+	odb_source_free(&source->base);
+}
+
+void test_odb_inmemory__read_empty_tree(void)
+{
+	struct odb_source_inmemory *source = odb_source_inmemory_new(odb);
+	cl_assert_object_info(source, repo.hash_algo->empty_tree, OBJ_TREE, "");
+	odb_source_free(&source->base);
+}
+
+void test_odb_inmemory__read_written_object(void)
+{
+	struct odb_source_inmemory *source = odb_source_inmemory_new(odb);
+	const char data[] = "foobar";
+	struct object_id written_oid;
+
+	cl_must_pass(odb_source_write_object(&source->base, data, strlen(data),
+					     OBJ_BLOB, &written_oid, NULL, 0));
+	cl_assert_equal_s(oid_to_hex(&written_oid), FOOBAR_OID);
+	cl_assert_object_info(source, &written_oid, OBJ_BLOB, "foobar");
+
+	odb_source_free(&source->base);
+}
+
+void test_odb_inmemory__read_stream_object(void)
+{
+	struct odb_source_inmemory *source = odb_source_inmemory_new(odb);
+	struct odb_read_stream *stream;
+	struct object_id written_oid;
+	const char data[] = "foobar";
+	char buf[3] = { 0 };
+
+	cl_must_pass(odb_source_write_object(&source->base, data, strlen(data),
+					     OBJ_BLOB, &written_oid, NULL, 0));
+
+	cl_must_pass(odb_source_read_object_stream(&stream, &source->base,
+						   &written_oid));
+	cl_assert_equal_i(stream->type, OBJ_BLOB);
+	cl_assert_equal_u(stream->size, 6);
+
+	cl_assert_equal_i(odb_read_stream_read(stream, buf, 2), 2);
+	cl_assert_equal_s(buf, "fo");
+	cl_assert_equal_i(odb_read_stream_read(stream, buf, 2), 2);
+	cl_assert_equal_s(buf, "ob");
+	cl_assert_equal_i(odb_read_stream_read(stream, buf, 2), 2);
+	cl_assert_equal_s(buf, "ar");
+	cl_assert_equal_i(odb_read_stream_read(stream, buf, 2), 0);
+
+	odb_read_stream_close(stream);
+	odb_source_free(&source->base);
+}
+
+static int add_one_object(const struct object_id *oid,
+			  struct object_info *oi UNUSED,
+			  void *payload)
+{
+	struct oidset *actual_oids = payload;
+	cl_must_pass(oidset_insert(actual_oids, oid));
+	return 0;
+}
+
+void test_odb_inmemory__for_each_object(void)
+{
+	struct odb_source_inmemory *source = odb_source_inmemory_new(odb);
+	struct odb_for_each_object_options opts = { 0 };
+	struct oidset expected_oids = OIDSET_INIT;
+	struct oidset actual_oids = OIDSET_INIT;
+	struct strbuf buf = STRBUF_INIT;
+
+	cl_must_pass(odb_source_for_each_object(&source->base, NULL,
+						add_one_object, &actual_oids, &opts));
+	cl_assert_equal_u(oidset_size(&actual_oids), 0);
+
+	for (int i = 0; i < 10; i++) {
+		struct object_id written_oid;
+
+		strbuf_reset(&buf);
+		strbuf_addf(&buf, "%d", i);
+
+		cl_must_pass(odb_source_write_object(&source->base, buf.buf, buf.len,
+						     OBJ_BLOB, &written_oid, NULL, 0));
+		cl_must_pass(oidset_insert(&expected_oids, &written_oid));
+	}
+
+	cl_must_pass(odb_source_for_each_object(&source->base, NULL,
+						add_one_object, &actual_oids, &opts));
+	cl_assert_equal_b(oidset_equal(&expected_oids, &actual_oids), true);
+
+	odb_source_free(&source->base);
+	oidset_clear(&expected_oids);
+	oidset_clear(&actual_oids);
+	strbuf_release(&buf);
+}
+
+static int abort_after_two_objects(const struct object_id *oid UNUSED,
+				   struct object_info *oi UNUSED,
+				   void *payload)
+{
+	unsigned *counter = payload;
+	(*counter)++;
+	if (*counter == 2)
+		return 123;
+	return 0;
+}
+
+void test_odb_inmemory__for_each_object_can_abort_iteration(void)
+{
+	struct odb_source_inmemory *source = odb_source_inmemory_new(odb);
+	struct odb_for_each_object_options opts = { 0 };
+	struct object_id written_oid;
+	unsigned counter = 0;
+
+	cl_must_pass(odb_source_write_object(&source->base, "1", 1,
+					     OBJ_BLOB, &written_oid, NULL, 0));
+	cl_must_pass(odb_source_write_object(&source->base, "2", 1,
+					     OBJ_BLOB, &written_oid, NULL, 0));
+	cl_must_pass(odb_source_write_object(&source->base, "3", 1,
+					     OBJ_BLOB, &written_oid, NULL, 0));
+
+	cl_assert_equal_i(odb_source_for_each_object(&source->base, NULL,
+						     abort_after_two_objects,
+						     &counter, &opts),
+			  123);
+	cl_assert_equal_u(counter, 2);
+
+	odb_source_free(&source->base);
+}
+
+void test_odb_inmemory__count_objects(void)
+{
+	struct odb_source_inmemory *source = odb_source_inmemory_new(odb);
+	struct object_id written_oid;
+	unsigned long count;
+
+	cl_must_pass(odb_source_count_objects(&source->base, 0, &count));
+	cl_assert_equal_u(count, 0);
+
+	cl_must_pass(odb_source_write_object(&source->base, "1", 1,
+					     OBJ_BLOB, &written_oid, NULL, 0));
+	cl_must_pass(odb_source_write_object(&source->base, "2", 1,
+					     OBJ_BLOB, &written_oid, NULL, 0));
+	cl_must_pass(odb_source_write_object(&source->base, "3", 1,
+					     OBJ_BLOB, &written_oid, NULL, 0));
+
+	cl_must_pass(odb_source_count_objects(&source->base, 0, &count));
+	cl_assert_equal_u(count, 3);
+
+	odb_source_free(&source->base);
+}
+
+void test_odb_inmemory__find_abbrev_len(void)
+{
+	struct odb_source_inmemory *source = odb_source_inmemory_new(odb);
+	struct object_id oid1, oid2;
+	unsigned abbrev_len;
+
+	/*
+	 * The two blobs we're about to write share the first 10 hex characters
+	 * of their object IDs ("a09f43dc45"), so at least 11 characters are
+	 * needed to tell them apart:
+	 *
+	 *   "368317" -> a09f43dc4562d45115583f5094640ae237df55f7
+	 *   "514796" -> a09f43dc45fef837235eb7e6b1a6ca5e169a3981
+	 *
+	 * With only one blob written we expect a length of 4.
+	 */
+	cl_must_pass(odb_source_write_object(&source->base, "368317", strlen("368317"),
+					     OBJ_BLOB, &oid1, NULL, 0));
+	cl_must_pass(odb_source_find_abbrev_len(&source->base, &oid1, 4,
+						&abbrev_len));
+	cl_assert_equal_u(abbrev_len, 4);
+
+	/*
+	 * With both objects present, the shared 10-character prefix means we
+	 * need at least 11 characters to uniquely identify either object.
+	 */
+	cl_must_pass(odb_source_write_object(&source->base, "514796", strlen("514796"),
+					     OBJ_BLOB, &oid2, NULL, 0));
+	cl_must_pass(odb_source_find_abbrev_len(&source->base, &oid1, 4,
+						&abbrev_len));
+	cl_assert_equal_u(abbrev_len, 11);
+
+	odb_source_free(&source->base);
+}
+
+void test_odb_inmemory__freshen_object(void)
+{
+	struct odb_source_inmemory *source = odb_source_inmemory_new(odb);
+	struct object_id written_oid;
+	struct object_id oid;
+	const char *end;
+
+	cl_must_pass(parse_oid_hex_algop(RANDOM_OID, &oid, &end, repo.hash_algo));
+	cl_assert_equal_i(odb_source_freshen_object(&source->base, &oid), 0);
+
+	cl_must_pass(odb_source_write_object(&source->base, "foobar",
+					     strlen("foobar"), OBJ_BLOB,
+					     &written_oid, NULL, 0));
+	cl_assert_equal_i(odb_source_freshen_object(&source->base,
+						    &written_oid), 1);
+
+	odb_source_free(&source->base);
+}
+
+struct membuf_write_stream {
+	struct odb_write_stream base;
+	const char *buf;
+	size_t offset;
+	size_t size;
+};
+
+static ssize_t membuf_write_stream_read(struct odb_write_stream *stream,
+					unsigned char *buf, size_t len)
+{
+	struct membuf_write_stream *s = container_of(stream, struct membuf_write_stream, base);
+	size_t chunk_size = 2;
+
+	if (chunk_size > len)
+		chunk_size = len;
+	if (chunk_size > s->size - s->offset)
+		chunk_size = s->size - s->offset;
+
+	memcpy(buf, s->buf + s->offset, chunk_size);
+
+	s->offset += chunk_size;
+	if (s->offset == s->size)
+		s->base.is_finished = 1;
+
+	return chunk_size;
+}
+
+void test_odb_inmemory__write_object_stream(void)
+{
+	struct odb_source_inmemory *source = odb_source_inmemory_new(odb);
+	const char data[] = "foobar";
+	struct membuf_write_stream stream = {
+		.base.read = membuf_write_stream_read,
+		.buf = data,
+		.size = strlen(data),
+	};
+	struct object_id written_oid;
+
+	cl_must_pass(odb_source_write_object_stream(&source->base, &stream.base,
+						    strlen(data), &written_oid));
+	cl_assert_equal_s(oid_to_hex(&written_oid), FOOBAR_OID);
+	cl_assert_object_info(source, &written_oid, OBJ_BLOB, "foobar");
+
+	odb_source_free(&source->base);
+}
diff --git a/t/unit-tests/u-oidtree.c b/t/unit-tests/u-oidtree.c
index e6eede2..f0d5ebb 100644
--- a/t/unit-tests/u-oidtree.c
+++ b/t/unit-tests/u-oidtree.c
@@ -19,12 +19,12 @@ static int fill_tree_loc(struct oidtree *ot, const char *hexes[], size_t n)
 	for (size_t i = 0; i < n; i++) {
 		struct object_id oid;
 		cl_parse_any_oid(hexes[i], &oid);
-		oidtree_insert(ot, &oid);
+		oidtree_insert(ot, &oid, NULL);
 	}
 	return 0;
 }
 
-static void check_contains(struct oidtree *ot, const char *hex, int expected)
+static void check_contains(struct oidtree *ot, const char *hex, bool expected)
 {
 	struct object_id oid;
 
@@ -38,9 +38,9 @@ struct expected_hex_iter {
 	const char *query;
 };
 
-static enum cb_next check_each_cb(const struct object_id *oid, void *data)
+static int check_each_cb(const struct object_id *oid, void *node_data UNUSED, void *cb_data)
 {
-	struct expected_hex_iter *hex_iter = data;
+	struct expected_hex_iter *hex_iter = cb_data;
 	struct object_id expected;
 
 	cl_assert(hex_iter->i < hex_iter->expected_hexes.nr);
@@ -49,7 +49,7 @@ static enum cb_next check_each_cb(const struct object_id *oid, void *data)
 			 &expected);
 	cl_assert_equal_s(oid_to_hex(oid), oid_to_hex(&expected));
 	hex_iter->i += 1;
-	return CB_CONTINUE;
+	return 0;
 }
 
 LAST_ARG_MUST_BE_NULL
@@ -88,12 +88,12 @@ void test_oidtree__cleanup(void)
 void test_oidtree__contains(void)
 {
 	FILL_TREE(&ot, "444", "1", "2", "3", "4", "5", "a", "b", "c", "d", "e");
-	check_contains(&ot, "44", 0);
-	check_contains(&ot, "441", 0);
-	check_contains(&ot, "440", 0);
-	check_contains(&ot, "444", 1);
-	check_contains(&ot, "4440", 1);
-	check_contains(&ot, "4444", 0);
+	check_contains(&ot, "44", false);
+	check_contains(&ot, "441", false);
+	check_contains(&ot, "440", false);
+	check_contains(&ot, "444", true);
+	check_contains(&ot, "4440", true);
+	check_contains(&ot, "4444", false);
 }
 
 void test_oidtree__each(void)
@@ -105,3 +105,23 @@ void test_oidtree__each(void)
 	check_each(&ot, "32100", "321", NULL);
 	check_each(&ot, "32", "320", "321", NULL);
 }
+
+void test_oidtree__insert_overwrites_data(void)
+{
+	struct object_id oid;
+	struct oidtree ot;
+	int a, b;
+
+	cl_parse_any_oid("1", &oid);
+
+	oidtree_init(&ot);
+
+	oidtree_insert(&ot, &oid, NULL);
+	cl_assert_equal_p(oidtree_get(&ot, &oid), NULL);
+	oidtree_insert(&ot, &oid, &a);
+	cl_assert_equal_p(oidtree_get(&ot, &oid), &a);
+	oidtree_insert(&ot, &oid, &b);
+	cl_assert_equal_p(oidtree_get(&ot, &oid), &b);
+
+	oidtree_clear(&ot);
+}
diff --git a/t/unit-tests/u-urlmatch-normalization.c b/t/unit-tests/u-urlmatch-normalization.c
index 39f6e1b..3595d89 100644
--- a/t/unit-tests/u-urlmatch-normalization.c
+++ b/t/unit-tests/u-urlmatch-normalization.c
@@ -245,3 +245,48 @@ void test_urlmatch_normalization__equivalents(void)
 	compare_normalized_urls("https://@x.y/^/../abc", "httpS://@x.y:0443/abc", 1);
 	compare_normalized_urls("https://@x.y/^/..", "httpS://@x.y:0443/", 1);
 }
+
+static void check_parsed_path(const char *url, const char *expected_path)
+{
+	struct url_info info;
+	char *parsed = url_parse(url, &info);
+	char *path;
+
+	cl_assert(parsed != NULL);
+	path = xstrndup(parsed + info.path_off, info.path_len);
+	cl_assert_equal_s(path, expected_path);
+	free(path);
+	free(parsed);
+}
+
+void test_urlmatch_normalization__parse_scp(void)
+{
+	check_parsed_path("host:path", "/path");
+	check_parsed_path("user@host:path", "/path");
+	check_parsed_path("host:~user/repo", "~user/repo");
+	check_parsed_path("user@host:~user/repo", "~user/repo");
+	check_parsed_path("[host]:src", "/src");
+	check_parsed_path("[host:123]:src", "/src");
+	check_parsed_path("[::1]:repo", "/repo");
+	check_parsed_path("user@[::1]:repo", "/repo");
+}
+
+void test_urlmatch_normalization__parse_url_form(void)
+{
+	check_parsed_path("ssh://host/repo", "/repo");
+	check_parsed_path("ssh://host/~user/repo", "~user/repo");
+	check_parsed_path("git://host:9418/repo", "/repo");
+	check_parsed_path("git://host/~user/repo", "~user/repo");
+	check_parsed_path("ssh://[::1]:1234/repo", "/repo");
+	check_parsed_path("http://[2001:db8::1]/repo", "/repo");
+}
+
+void test_urlmatch_normalization__parse_strips_query_and_fragment(void)
+{
+	check_parsed_path("ssh://host/~user/repo?q", "~user/repo");
+	check_parsed_path("ssh://host/~user/repo#frag", "~user/repo");
+	check_parsed_path("git://host/~user/repo?q", "~user/repo");
+	check_parsed_path("user@host:~user/repo?q", "~user/repo");
+	check_parsed_path("https://host/repo?q", "/repo");
+	check_parsed_path("https://host/repo#frag", "/repo");
+}
diff --git a/tempfile.c b/tempfile.c
index 82dfa3d..f0fdf58 100644
--- a/tempfile.c
+++ b/tempfile.c
@@ -373,3 +373,15 @@ int delete_tempfile(struct tempfile **tempfile_p)
 
 	return err ? -1 : 0;
 }
+
+void reassign_tempfile_ownership(pid_t from, pid_t to)
+{
+	volatile struct volatile_list_head *pos;
+
+	list_for_each(pos, &tempfile_list) {
+		struct tempfile *p = list_entry(pos, struct tempfile, list);
+
+		if (is_tempfile_active(p) && p->owner == from)
+			p->owner = to;
+	}
+}
diff --git a/tempfile.h b/tempfile.h
index 2d2ae5b..2227a09 100644
--- a/tempfile.h
+++ b/tempfile.h
@@ -282,4 +282,15 @@ int delete_tempfile(struct tempfile **tempfile_p);
  */
 int rename_tempfile(struct tempfile **tempfile_p, const char *path);
 
+/*
+ * Reassign ownership of all active tempfiles whose `owner` field matches
+ * `from` to `to`.
+ *
+ * This is intended for use by `daemonize()`; after `fork(2)`-ing, the parent
+ * transfers ownership to the daemonized child so that its atexit handler does
+ * not unlink tempfiles that should outlive it, and the child claims the
+ * inherited tempfiles so that they are cleaned up when the daemon exits.
+ */
+void reassign_tempfile_ownership(pid_t from, pid_t to);
+
 #endif /* TEMPFILE_H */
diff --git a/tools/generate-configlist.sh b/tools/generate-configlist.sh
index e28054f..d1d2ba4 100755
--- a/tools/generate-configlist.sh
+++ b/tools/generate-configlist.sh
@@ -42,9 +42,12 @@
 then
 	QUOTED_OUTPUT="$(printf '%s\n' "$OUTPUT" | sed 's,[&/\],\\&,g')"
 	{
+		printf '%s' "$QUOTED_OUTPUT: "
 		printf '%s\n' "$SOURCE_DIR"/Documentation/*config.adoc \
 			"$SOURCE_DIR"/Documentation/config/*.adoc |
-			sed -e 's/[# ]/\\&/g' -e "s/^/$QUOTED_OUTPUT: /"
+			sed -e 's/[# ]/\\&/g' |
+			tr '\n' ' '
+		printf '\n'
 		printf '%s:\n' "$SOURCE_DIR"/Documentation/*config.adoc \
 			"$SOURCE_DIR"/Documentation/config/*.adoc |
 			sed -e 's/[# ]/\\&/g'
diff --git a/trailer.c b/trailer.c
index 470f86a..6d8ec7f 100644
--- a/trailer.c
+++ b/trailer.c
@@ -988,10 +988,9 @@ static int ends_with_blank_line(const char *buf, size_t len)
 
 static void unfold_value(struct strbuf *val)
 {
-	struct strbuf out = STRBUF_INIT;
 	size_t i;
+	size_t pos = 0;
 
-	strbuf_grow(&out, val->len);
 	i = 0;
 	while (i < val->len) {
 		char c = val->buf[i++];
@@ -999,18 +998,14 @@ static void unfold_value(struct strbuf *val)
 			/* Collapse continuation down to a single space. */
 			while (i < val->len && isspace(val->buf[i]))
 				i++;
-			strbuf_addch(&out, ' ');
-		} else {
-			strbuf_addch(&out, c);
+			c = ' ';
 		}
+		val->buf[pos++] = c;
 	}
+	strbuf_setlen(val, pos);
 
 	/* Empty lines may have left us with whitespace cruft at the edges */
-	strbuf_trim(&out);
-
-	/* output goes back to val as if we modified it in-place */
-	strbuf_swap(&out, val);
-	strbuf_release(&out);
+	strbuf_trim(val);
 }
 
 static struct trailer_block *trailer_block_new(void)
diff --git a/transport-helper.c b/transport-helper.c
index 4d95d84..04d5557 100644
--- a/transport-helper.c
+++ b/transport-helper.c
@@ -620,8 +620,22 @@ static int run_connect(struct transport *transport, struct strbuf *cmdbuf)
 	return ret;
 }
 
+static const char *connect_service_cmd(enum git_connect_service service)
+{
+	switch (service) {
+	case GIT_CONNECT_UPLOAD_PACK:
+		return "git-upload-pack";
+	case GIT_CONNECT_RECEIVE_PACK:
+		return "git-receive-pack";
+	case GIT_CONNECT_UPLOAD_ARCHIVE:
+		return "git-upload-archive";
+	}
+	BUG("unknown git_connect_service: %d", service);
+}
+
 static int process_connect_service(struct transport *transport,
-				   const char *name, const char *exec)
+				   enum git_connect_service service,
+				   const char *exec)
 {
 	struct helper_data *data = transport->data;
 	struct strbuf cmdbuf = STRBUF_INIT;
@@ -631,7 +645,7 @@ static int process_connect_service(struct transport *transport,
 	 * Handle --upload-pack and friends. This is fire and forget...
 	 * just warn if it fails.
 	 */
-	if (strcmp(name, exec)) {
+	if (strcmp(connect_service_cmd(service), exec)) {
 		int r = set_helper_option(transport, "servpath", exec);
 		if (r > 0)
 			warning(_("setting remote service path not supported by protocol"));
@@ -640,13 +654,15 @@ static int process_connect_service(struct transport *transport,
 	}
 
 	if (data->connect) {
-		strbuf_addf(&cmdbuf, "connect %s\n", name);
+		strbuf_addf(&cmdbuf, "connect %s\n",
+			    connect_service_cmd(service));
 		ret = run_connect(transport, &cmdbuf);
 	} else if (data->stateless_connect &&
 		   (get_protocol_version_config() == protocol_v2) &&
-		   (!strcmp("git-upload-pack", name) ||
-		    !strcmp("git-upload-archive", name))) {
-		strbuf_addf(&cmdbuf, "stateless-connect %s\n", name);
+		   (service == GIT_CONNECT_UPLOAD_PACK ||
+		    service == GIT_CONNECT_UPLOAD_ARCHIVE)) {
+		strbuf_addf(&cmdbuf, "stateless-connect %s\n",
+			    connect_service_cmd(service));
 		ret = run_connect(transport, &cmdbuf);
 		if (ret)
 			transport->stateless_rpc = 1;
@@ -660,32 +676,33 @@ static int process_connect(struct transport *transport,
 				     int for_push)
 {
 	struct helper_data *data = transport->data;
-	const char *name;
+	enum git_connect_service service;
 	const char *exec;
 	int ret;
 
-	name = for_push ? "git-receive-pack" : "git-upload-pack";
+	service = for_push ? GIT_CONNECT_RECEIVE_PACK : GIT_CONNECT_UPLOAD_PACK;
 	if (for_push)
 		exec = data->transport_options.receivepack;
 	else
 		exec = data->transport_options.uploadpack;
 
-	ret = process_connect_service(transport, name, exec);
+	ret = process_connect_service(transport, service, exec);
 	if (ret)
 		do_take_over(transport);
 	return ret;
 }
 
-static int connect_helper(struct transport *transport, const char *name,
-		   const char *exec, int fd[2])
+static int connect_helper(struct transport *transport, enum git_connect_service service,
+			  const char *exec, int fd[2])
 {
 	struct helper_data *data = transport->data;
 
 	/* Get_helper so connect is inited. */
 	get_helper(transport);
 
-	if (!process_connect_service(transport, name, exec))
-		die(_("can't connect to subservice %s"), name);
+	if (!process_connect_service(transport, service, exec))
+		die(_("can't connect to subservice %s"),
+		    connect_service_cmd(service));
 
 	fd[0] = data->helper->out;
 	fd[1] = data->helper->in;
@@ -754,8 +771,9 @@ static int fetch_refs(struct transport *transport,
 		set_helper_option(transport, "filter", spec);
 	}
 
-	if (data->transport_options.negotiation_tips)
-		warning("Ignoring --negotiation-tip because the protocol does not support it.");
+	if (data->transport_options.negotiation_restrict_tips)
+		warning(_("ignoring %s because the protocol does not support it."),
+			"--negotiation-restrict");
 
 	if (data->fetch)
 		return fetch_with_fetch(transport, nr_heads, to_fetch);
@@ -781,7 +799,8 @@ static int push_update_ref_status(struct strbuf *buf,
 
 	if (starts_with(buf->buf, "option ")) {
 		struct object_id old_oid, new_oid;
-		const char *key, *val;
+		char *key;
+		const char *val;
 		char *p;
 
 		if (!state->hint || !(state->report || state->new_report))
diff --git a/transport-internal.h b/transport-internal.h
index 90ea749..051f3ab 100644
--- a/transport-internal.h
+++ b/transport-internal.h
@@ -1,6 +1,8 @@
 #ifndef TRANSPORT_INTERNAL_H
 #define TRANSPORT_INTERNAL_H
 
+#include "connect.h"
+
 struct ref;
 struct transport;
 struct strvec;
@@ -58,7 +60,8 @@ struct transport_vtable {
 	 * process involved generating new commits.
 	 **/
 	int (*push_refs)(struct transport *transport, struct ref *refs, int flags);
-	int (*connect)(struct transport *connection, const char *name,
+	int (*connect)(struct transport *connection,
+		       enum git_connect_service service,
 		       const char *executable, int fd[2]);
 
 	/** get_refs_list(), fetch(), and push_refs() can keep
diff --git a/transport.c b/transport.c
index e53936d..0f5ec30 100644
--- a/transport.c
+++ b/transport.c
@@ -29,6 +29,7 @@
 #include "object-name.h"
 #include "color.h"
 #include "bundle-uri.h"
+#include "sideband.h"
 
 static enum git_colorbool transport_use_color = GIT_COLOR_UNKNOWN;
 static char transport_colors[][COLOR_MAXLEN] = {
@@ -308,8 +309,8 @@ static int connect_setup(struct transport *transport, int for_push)
 
 	data->conn = git_connect(data->fd, transport->url,
 				 for_push ?
-					"git-receive-pack" :
-					"git-upload-pack",
+					GIT_CONNECT_RECEIVE_PACK :
+					GIT_CONNECT_UPLOAD_PACK,
 				 for_push ?
 					data->options.receivepack :
 					data->options.uploadpack,
@@ -463,7 +464,8 @@ static int fetch_refs_via_pack(struct transport *transport,
 	args.refetch = data->options.refetch;
 	args.stateless_rpc = transport->stateless_rpc;
 	args.server_options = transport->server_options;
-	args.negotiation_tips = data->options.negotiation_tips;
+	args.negotiation_restrict_tips = data->options.negotiation_restrict_tips;
+	args.negotiation_include_tips = data->options.negotiation_include_tips;
 	args.reject_shallow_remote = transport->smart_options->reject_shallow;
 
 	if (!data->finished_handshake) {
@@ -491,11 +493,12 @@ static int fetch_refs_via_pack(struct transport *transport,
 			warning(_("server does not support wait-for-done"));
 			ret = -1;
 		} else {
-			negotiate_using_fetch(data->options.negotiation_tips,
+			negotiate_using_fetch(data->options.negotiation_restrict_tips,
 					      transport->server_options,
 					      transport->stateless_rpc,
 					      data->fd,
-					      data->options.acked_commits);
+					      data->options.acked_commits,
+					      data->options.negotiation_include_tips);
 			ret = 0;
 		}
 		goto cleanup;
@@ -919,6 +922,8 @@ static int git_transport_push(struct transport *transport, struct ref *remote_re
 	args.atomic = !!(flags & TRANSPORT_PUSH_ATOMIC);
 	args.push_options = transport->push_options;
 	args.url = transport->url;
+	args.negotiation_include = &transport->remote->negotiation_include;
+	args.negotiation_restrict = &transport->remote->negotiation_restrict;
 
 	if (flags & TRANSPORT_PUSH_CERT_ALWAYS)
 		args.push_cert = SEND_PACK_PUSH_CERT_ALWAYS;
@@ -956,12 +961,13 @@ static int git_transport_push(struct transport *transport, struct ref *remote_re
 	return ret;
 }
 
-static int connect_git(struct transport *transport, const char *name,
+static int connect_git(struct transport *transport,
+		       enum git_connect_service service,
 		       const char *executable, int fd[2])
 {
 	struct git_transport_data *data = transport->data;
 	data->conn = git_connect(data->fd, transport->url,
-				 name, executable, 0);
+				 service, executable, 0);
 	fd[0] = data->fd[0];
 	fd[1] = data->fd[1];
 	return 0;
@@ -979,9 +985,13 @@ static int disconnect_git(struct transport *transport)
 		finish_connect(data->conn);
 	}
 
-	if (data->options.negotiation_tips) {
-		oid_array_clear(data->options.negotiation_tips);
-		free(data->options.negotiation_tips);
+	if (data->options.negotiation_restrict_tips) {
+		oid_array_clear(data->options.negotiation_restrict_tips);
+		free(data->options.negotiation_restrict_tips);
+	}
+	if (data->options.negotiation_include_tips) {
+		oid_array_clear(data->options.negotiation_include_tips);
+		free(data->options.negotiation_include_tips);
 	}
 	list_objects_filter_release(&data->options.filter_options);
 	oid_array_clear(&data->extra_have);
@@ -1246,6 +1256,8 @@ struct transport *transport_get(struct remote *remote, const char *url)
 
 	ret->hash_algo = &hash_algos[GIT_HASH_SHA1_LEGACY];
 
+	sideband_apply_url_config(ret->url);
+
 	return ret;
 }
 
@@ -1391,8 +1403,10 @@ static int run_pre_push_hook(struct transport *transport,
 	opt.feed_pipe_cb_data_free = pre_push_hook_data_free;
 
 	/*
-	 * pre-push hooks expect stdout & stderr to be separate, so don't merge
-	 * them to keep backwards compatibility with existing hooks.
+	 * pre-push hooks keep stdout and stderr separate by default for
+	 * backwards compatibility. When the user opts into parallel execution
+	 * via hook.jobs > 1 or -j, get_hook_jobs() will set stdout_to_stderr=1
+	 * automatically so run-command can de-interleave the outputs.
 	 */
 	opt.stdout_to_stderr = 0;
 
@@ -1651,11 +1665,12 @@ void transport_unlock_pack(struct transport *transport, unsigned int flags)
 		string_list_clear(&transport->pack_lockfiles, 0);
 }
 
-int transport_connect(struct transport *transport, const char *name,
+int transport_connect(struct transport *transport,
+		      enum git_connect_service service,
 		      const char *exec, int fd[2])
 {
 	if (transport->vtable->connect)
-		return transport->vtable->connect(transport, name, exec, fd);
+		return transport->vtable->connect(transport, service, exec, fd);
 	else
 		die(_("operation not supported by protocol"));
 }
diff --git a/transport.h b/transport.h
index 892f194..7e5867c 100644
--- a/transport.h
+++ b/transport.h
@@ -5,6 +5,7 @@
 #include "remote.h"
 #include "list-objects-filter-options.h"
 #include "string-list.h"
+#include "connect.h"
 
 struct git_transport_options {
 	unsigned thin : 1;
@@ -40,13 +41,14 @@ struct git_transport_options {
 
 	/*
 	 * This is only used during fetch. See the documentation of
-	 * negotiation_tips in struct fetch_pack_args.
+	 * these member names in struct fetch_pack_args.
 	 *
-	 * This field is only supported by transports that support connect or
+	 * These fields are only supported by transports that support connect or
 	 * stateless_connect. Set this field directly instead of using
 	 * transport_set_option().
 	 */
-	struct oid_array *negotiation_tips;
+	struct oid_array *negotiation_restrict_tips;
+	struct oid_array *negotiation_include_tips;
 
 	/*
 	 * If allocated, whenever transport_fetch_refs() is called, add known
@@ -324,7 +326,8 @@ char *transport_anonymize_url(const char *url);
 void transport_take_over(struct transport *transport,
 			 struct child_process *child);
 
-int transport_connect(struct transport *transport, const char *name,
+int transport_connect(struct transport *transport,
+		      enum git_connect_service service,
 		      const char *exec, int fd[2]);
 
 /* Transport methods defined outside transport.c */
diff --git a/upload-pack.c b/upload-pack.c
index 9f6d6fe..2bf450a 100644
--- a/upload-pack.c
+++ b/upload-pack.c
@@ -886,7 +886,7 @@ static void deepen(struct upload_pack_data *data, int depth)
 					     data->deepen_relative, depth,
 					     SHALLOW, NOT_SHALLOW);
 		send_shallow(data, result);
-		free_commit_list(result);
+		commit_list_free(result);
 	}
 
 	send_unshallow(data);
@@ -900,7 +900,7 @@ static void deepen_by_rev_list(struct upload_pack_data *data,
 	disable_commit_graph(the_repository);
 	result = get_shallow_commits_by_rev_list(argv, SHALLOW, NOT_SHALLOW);
 	send_shallow(data, result);
-	free_commit_list(result);
+	commit_list_free(result);
 	send_unshallow(data);
 }
 
diff --git a/url.c b/url.c
index 3ca5987..a598182 100644
--- a/url.c
+++ b/url.c
@@ -132,3 +132,26 @@ void str_end_url_with_slash(const char *url, char **dest)
 	free(*dest);
 	*dest = strbuf_detach(&buf, NULL);
 }
+
+int url_is_local_not_ssh(const char *url)
+{
+	const char *colon = strchr(url, ':');
+	const char *slash = strchr(url, '/');
+	return !colon || (slash && slash < colon) ||
+		(has_dos_drive_prefix(url) && is_valid_path(url));
+}
+
+enum url_scheme url_get_scheme(const char *name)
+{
+	if (!strcmp(name, "ssh"))
+		return URL_SCHEME_SSH;
+	if (!strcmp(name, "git"))
+		return URL_SCHEME_GIT;
+	if (!strcmp(name, "git+ssh")) /* deprecated - do not use */
+		return URL_SCHEME_SSH;
+	if (!strcmp(name, "ssh+git")) /* deprecated - do not use */
+		return URL_SCHEME_SSH;
+	if (!strcmp(name, "file"))
+		return URL_SCHEME_FILE;
+	return URL_SCHEME_UNKNOWN;
+}
diff --git a/url.h b/url.h
index cd9140e..7289523 100644
--- a/url.h
+++ b/url.h
@@ -21,6 +21,22 @@ char *url_decode_parameter_value(const char **query);
 void end_url_with_slash(struct strbuf *buf, const char *url);
 void str_end_url_with_slash(const char *url, char **dest);
 
+int url_is_local_not_ssh(const char *url);
+
+enum url_scheme {
+	URL_SCHEME_UNKNOWN = 0,
+	URL_SCHEME_LOCAL,
+	URL_SCHEME_FILE,
+	URL_SCHEME_SSH,
+	URL_SCHEME_GIT,
+};
+
+/*
+ * Identify the URL scheme by name. Returns URL_SCHEME_UNKNOWN
+ * if the name does not match any scheme that Git knows about.
+ */
+enum url_scheme url_get_scheme(const char *name);
+
 /*
  * The set of unreserved characters as per STD66 (RFC3986) is
  * '[A-Za-z0-9-._~]'. These characters are safe to appear in URI
diff --git a/urlmatch.c b/urlmatch.c
index eea8300..bf8cce6 100644
--- a/urlmatch.c
+++ b/urlmatch.c
@@ -5,6 +5,7 @@
 #include "hex-ll.h"
 #include "strbuf.h"
 #include "urlmatch.h"
+#include "url.h"
 
 #define URL_ALPHA "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
 #define URL_DIGIT "0123456789"
@@ -440,6 +441,132 @@ char *url_normalize(const char *url, struct url_info *out_info)
 	return url_normalize_1(url, out_info, 0);
 }
 
+char *url_parse(const char *url_orig, struct url_info *out_info)
+{
+	struct strbuf url;
+	char *host, *separator;
+	char *detached, *normalized;
+	char *url_decoded;
+	enum url_scheme scheme = URL_SCHEME_LOCAL;
+	struct url_info local_info;
+	struct url_info *info = out_info ? out_info : &local_info;
+	bool scp_syntax = false;
+
+	if (is_url(url_orig))
+		url_decoded = url_decode(url_orig);
+	else
+		url_decoded = xstrdup(url_orig);
+
+	strbuf_init(&url, strlen(url_decoded) + sizeof("ssh://"));
+	strbuf_addstr(&url, url_decoded);
+	free(url_decoded);
+
+	host = strstr(url.buf, "://");
+	if (host) {
+		/*
+		 * Temporarily NUL-terminate the scheme name
+		 * so we can pass it to url_get_scheme(),
+		 * then restore the ':' so the buffer
+		 * is intact for url_normalize() below.
+		 */
+		char saved = *host;
+		*host = '\0';
+		scheme = url_get_scheme(url.buf);
+		*host = saved;
+		host += 3;
+	} else {
+		if (!url_is_local_not_ssh(url.buf)) {
+			scp_syntax = true;
+			scheme = URL_SCHEME_SSH;
+			strbuf_insertstr(&url, 0, "ssh://");
+			host = url.buf + strlen("ssh://");
+		}
+	}
+
+	/*
+	 * Path starts after ':' in scp style SSH URLs.
+	 *
+	 * The host portion can begin with an optional "user@",
+	 * and the host itself can be wrapped in '[' ']' brackets.
+	 * The bracket form is git's legacy way of supporting:
+	 *
+	 *   - IPv6 literals: [::1]:repo
+	 *   - host:port pairs in the short form: [myhost:123]:src
+	 *   - Plain hostnames that happen to need bracketing: [host]:path
+	 *
+	 * Treat '[' followed by 0 or 1 inner colons as the host:port
+	 * or plain hostname form and strip the brackets so url_normalize
+	 * sees host[:port] natively. Two or more inner colons mark an
+	 * IPv6 literal: keep the brackets for url_normalize to recognize.
+	 *
+	 * The scp path separator is the ':' that follows the host part,
+	 * and we must skip over user@ and any '[...]' before searching.
+	 */
+	if (scp_syntax) {
+		char *user_at;
+		char *host_start;
+		char *bracket_end;
+
+		user_at = strchr(host, '@');
+		host_start = user_at ? user_at + 1 : host;
+
+		if (*host_start == '[') {
+			char *p;
+			int inner_colons;
+
+			bracket_end = strchr(host_start, ']');
+			inner_colons = 0;
+			for (p = host_start + 1; bracket_end && p < bracket_end; p++)
+				if (*p == ':')
+					inner_colons++;
+
+			if (bracket_end && inner_colons <= 1) {
+				size_t close_off = bracket_end - url.buf;
+				size_t open_off = host_start - url.buf;
+				strbuf_remove(&url, close_off, 1);
+				strbuf_remove(&url, open_off, 1);
+				separator = url.buf + close_off - 1;
+			} else if (bracket_end) {
+				separator = strchr(bracket_end + 1, ':');
+			} else {
+				separator = strchr(host_start, ':');
+			}
+		} else {
+			separator = strchr(host_start, ':');
+		}
+
+		if (separator) {
+			if (separator[1] == '/')
+				strbuf_remove(&url, separator - url.buf, 1);
+			else
+				*separator = '/';
+		}
+	}
+
+	detached = strbuf_detach(&url, NULL);
+	normalized = url_normalize(detached, info);
+	free(detached);
+
+	if (!normalized)
+		return NULL;
+
+	/*
+	 * Point path to ~ for URLs like this:
+	 *
+	 *     ssh://host.xz/~user/repo
+	 *     git://host.xz/~user/repo
+	 *     host.xz:~user/repo
+	 */
+	if (scheme == URL_SCHEME_GIT || scheme == URL_SCHEME_SSH) {
+		if (normalized[info->path_off + 1] == '~') {
+			info->path_off++;
+			info->path_len--;
+		}
+	}
+
+	return normalized;
+}
+
 static size_t url_match_prefix(const char *url,
 			       const char *url_prefix,
 			       size_t url_prefix_len)
diff --git a/urlmatch.h b/urlmatch.h
index 5ba85ce..6b3ce42 100644
--- a/urlmatch.h
+++ b/urlmatch.h
@@ -35,6 +35,7 @@ struct url_info {
 };
 
 char *url_normalize(const char *, struct url_info *);
+char *url_parse(const char *, struct url_info *);
 
 struct urlmatch_item {
 	size_t hostmatch_len;
diff --git a/userdiff.c b/userdiff.c
index fe710a6..b5412e6 100644
--- a/userdiff.c
+++ b/userdiff.c
@@ -344,14 +344,24 @@ PATTERNS("rust",
 	 "|[0-9][0-9_a-fA-Fiosuxz]*(\\.([0-9]*[eE][+-]?)?[0-9_fF]*)?"
 	 "|[-+*\\/<>%&^|=!:]=|<<=?|>>=?|&&|\\|\\||->|=>|\\.{2}=|\\.{3}|::"),
 PATTERNS("scheme",
-	 "^[\t ]*(\\(((define|def(struct|syntax|class|method|rules|record|proto|alias)?)[-*/ \t]|(library|module|struct|class)[*+ \t]).*)$",
 	 /*
-	  * R7RS valid identifiers include any sequence enclosed
-	  * within vertical lines having no backslashes
+	  * An unindented opening parenthesis identifies a top-level
+	  * expression in all Lisp dialects.
 	  */
-	 "\\|([^\\\\]*)\\|"
-	 /* All other words should be delimited by spaces or parentheses */
-	 "|([^][)(}{[ \t])+"),
+	 "^(\\(.*)$\n"
+	 /* For Scheme: a possibly indented left paren followed by a keyword. */
+	 "^[\t ]*(\\(((define|def(struct|syntax|class|method|rules|record|proto|alias)?)[-*/ \t]|(library|module|struct|class)[*+ \t]).*)$\n"
+	 /*
+	  * For all Lisp dialects: a slightly indented line starting with "(def".
+	  */
+	 "^  ?(\\([Dd][Ee][Ff].*)$",
+	 /*
+	  * The union of R7RS and Common Lisp symbol syntax: allows arbitrary
+	  * strings between vertical bars, including any escaped characters.
+	  */
+	 "\\|([^|\\\\]|\\\\.)*\\|"
+	 /* All other words should be delimited by spaces or parentheses. */
+	 "|([^][)(}{ \t])+"),
 PATTERNS("tex", "^(\\\\((sub)*section|chapter|part)\\*{0,1}\\{.*)$",
 	 "\\\\[a-zA-Z@]+|\\\\.|([a-zA-Z0-9]|[^\x01-\x7f])+"),
 { .name = "default", .binary = -1 },
diff --git a/walker.c b/walker.c
index 9133253..e98eb6d 100644
--- a/walker.c
+++ b/walker.c
@@ -155,7 +155,7 @@ static int process(struct walker *walker, struct object *obj)
 	obj->flags |= SEEN;
 
 	if (odb_has_object(the_repository->objects, &obj->oid,
-			   HAS_OBJECT_RECHECK_PACKED | HAS_OBJECT_FETCH_PROMISOR)) {
+			   ODB_HAS_OBJECT_RECHECK_PACKED | ODB_HAS_OBJECT_FETCH_PROMISOR)) {
 		/* We already have it, so we should scan it now. */
 		obj->flags |= TO_SCAN;
 	}
diff --git a/worktree.c b/worktree.c
index d874e23..97eddc3 100644
--- a/worktree.c
+++ b/worktree.c
@@ -66,7 +66,7 @@ static int is_current_worktree(struct worktree *wt)
 	return is_current;
 }
 
-struct worktree *get_worktree_from_repository(struct repository *repo)
+struct worktree *get_current_worktree(struct repository *repo)
 {
 	struct worktree *wt = xcalloc(1, sizeof(*wt));
 	char *gitdir = absolute_pathdup(repo->gitdir);
@@ -1104,7 +1104,7 @@ void write_worktree_linking_files(const char *dotgit, const char *gitdir,
 	strbuf_realpath(&repo, repo.buf, 1);
 
 	if (use_relative_paths && !the_repository->repository_format_relative_worktrees) {
-		if (upgrade_repository_format(1) < 0)
+		if (upgrade_repository_format(the_repository, 1) < 0)
 			die(_("unable to upgrade repository format to support relative worktrees"));
 		if (repo_config_set_gently(the_repository, "extensions.relativeWorktrees", "true"))
 			die(_("unable to set extensions.relativeWorktrees setting"));
diff --git a/worktree.h b/worktree.h
index d19ec29..1075409 100644
--- a/worktree.h
+++ b/worktree.h
@@ -42,7 +42,7 @@ struct worktree **get_worktrees_without_reading_head(void);
  * Construct a struct worktree corresponding to repo->gitdir and
  * repo->worktree.
  */
-struct worktree *get_worktree_from_repository(struct repository *repo);
+struct worktree *get_current_worktree(struct repository *repo);
 
 /*
  * Returns 1 if linked worktrees exist, 0 otherwise.
diff --git a/wrapper.c b/wrapper.c
index be8fa57..16f5a63 100644
--- a/wrapper.c
+++ b/wrapper.c
@@ -323,47 +323,6 @@ ssize_t write_in_full(int fd, const void *buf, size_t count)
 	return total;
 }
 
-ssize_t writev_in_full(int fd, struct iovec *iov, int iovcnt)
-{
-	ssize_t total_written = 0;
-
-	while (iovcnt) {
-		ssize_t bytes_written = writev(fd, iov, iovcnt);
-		if (bytes_written < 0) {
-			if (errno == EINTR || errno == EAGAIN)
-				continue;
-			return -1;
-		}
-		if (!bytes_written) {
-			errno = ENOSPC;
-			return -1;
-		}
-
-		total_written += bytes_written;
-
-		/*
-		 * We first need to discard any iovec entities that have been
-		 * fully written.
-		 */
-		while (iovcnt && (size_t)bytes_written >= iov->iov_len) {
-			bytes_written -= iov->iov_len;
-			iov++;
-			iovcnt--;
-		}
-
-		/*
-		 * Finally, we need to adjust the last iovec in case we have
-		 * performed a partial write.
-		 */
-		if (iovcnt && bytes_written) {
-			iov->iov_base = (char *) iov->iov_base + bytes_written;
-			iov->iov_len -= bytes_written;
-		}
-	}
-
-	return total_written;
-}
-
 ssize_t pread_in_full(int fd, void *buf, size_t count, off_t offset)
 {
 	char *p = buf;
diff --git a/wrapper.h b/wrapper.h
index 27519b3..15ac3ba 100644
--- a/wrapper.h
+++ b/wrapper.h
@@ -47,15 +47,6 @@ ssize_t read_in_full(int fd, void *buf, size_t count);
 ssize_t write_in_full(int fd, const void *buf, size_t count);
 ssize_t pread_in_full(int fd, void *buf, size_t count, off_t offset);
 
-/*
- * Try to write all iovecs. Returns -1 in case an error occurred with a proper
- * errno set, the number of bytes written otherwise.
- *
- * Note that the iovec will be modified as a result of this call to adjust for
- * partial writes!
- */
-ssize_t writev_in_full(int fd, struct iovec *iov, int iovcnt);
-
 static inline ssize_t write_str_in_full(int fd, const char *str)
 {
 	return write_in_full(fd, str, strlen(str));
diff --git a/write-or-die.c b/write-or-die.c
index 5f522fb..01a9a51 100644
--- a/write-or-die.c
+++ b/write-or-die.c
@@ -96,14 +96,6 @@ void write_or_die(int fd, const void *buf, size_t count)
 	}
 }
 
-void writev_or_die(int fd, struct iovec *iov, int iovlen)
-{
-	if (writev_in_full(fd, iov, iovlen) < 0) {
-		check_pipe(errno);
-		die_errno("writev error");
-	}
-}
-
 void fwrite_or_die(FILE *f, const void *buf, size_t count)
 {
 	if (fwrite(buf, 1, count, f) != count)
diff --git a/write-or-die.h b/write-or-die.h
index a045bdf..ff0408b 100644
--- a/write-or-die.h
+++ b/write-or-die.h
@@ -7,7 +7,6 @@ void fprintf_or_die(FILE *, const char *fmt, ...);
 void fwrite_or_die(FILE *f, const void *buf, size_t count);
 void fflush_or_die(FILE *f);
 void write_or_die(int fd, const void *buf, size_t count);
-void writev_or_die(int fd, struct iovec *iov, int iovlen);
 
 /*
  * These values are used to help identify parts of a repository to fsync.
diff --git a/wt-status.c b/wt-status.c
index 479ccc3..b173723 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -1206,7 +1206,7 @@ static void wt_longstatus_print_verbose(struct wt_status *s)
 		status_printf_ln(s, c,
 			"--------------------------------------------------");
 		status_printf_ln(s, c, _("Changes not staged for commit:"));
-		setup_work_tree();
+		setup_work_tree(the_repository);
 		rev.diffopt.a_prefix = "i/";
 		rev.diffopt.b_prefix = "w/";
 		run_diff_files(&rev, 0);
@@ -1827,7 +1827,7 @@ void wt_status_get_state(struct repository *r,
 	struct stat st;
 	struct object_id oid;
 	enum replay_action action;
-	struct worktree *wt = get_worktree_from_repository(r);
+	struct worktree *wt = get_current_worktree(r);
 
 	if (!stat(git_path_merge_head(r), &st)) {
 		wt_status_check_rebase(wt, state);
diff --git a/xdiff-interface.c b/xdiff-interface.c
index f043330..5ee2b96 100644
--- a/xdiff-interface.c
+++ b/xdiff-interface.c
@@ -325,6 +325,18 @@ int parse_conflict_style_name(const char *value)
 		return -1;
 }
 
+const char *conflict_style_name(int style)
+{
+	switch (style) {
+	case XDL_MERGE_DIFF3:
+		return "diff3";
+	case XDL_MERGE_ZEALOUS_DIFF3:
+		return "zdiff3";
+	default:
+		return "merge";
+	}
+}
+
 int git_xmerge_style = -1;
 
 int git_xmerge_config(const char *var, const char *value,
diff --git a/xdiff-interface.h b/xdiff-interface.h
index fbc4cee..ce54e1c 100644
--- a/xdiff-interface.h
+++ b/xdiff-interface.h
@@ -55,6 +55,7 @@ void xdiff_set_find_func(xdemitconf_t *xecfg, const char *line, int cflags);
 void xdiff_clear_find_func(xdemitconf_t *xecfg);
 struct config_context;
 int parse_conflict_style_name(const char *value);
+const char *conflict_style_name(int style);
 int git_xmerge_config(const char *var, const char *value,
 		      const struct config_context *ctx, void *cb);
 extern int git_xmerge_style;
diff --git a/xdiff/xdiffi.c b/xdiff/xdiffi.c
index 5455b46..c5a892f 100644
--- a/xdiff/xdiffi.c
+++ b/xdiff/xdiffi.c
@@ -348,7 +348,7 @@ int xdl_do_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,
 	kvdf += xe->xdf2.nreff + 1;
 	kvdb += xe->xdf2.nreff + 1;
 
-	xenv.mxcost = xdl_bogosqrt(ndiags);
+	xenv.mxcost = (long)xdl_bogosqrt((uint64_t)ndiags);
 	if (xenv.mxcost < XDL_MAX_COST_MIN)
 		xenv.mxcost = XDL_MAX_COST_MIN;
 	xenv.snake_cnt = XDL_SNAKE_CNT;
diff --git a/xdiff/xemit.c b/xdiff/xemit.c
index 04f7e91..7cd9cf0 100644
--- a/xdiff/xemit.c
+++ b/xdiff/xemit.c
@@ -46,12 +46,20 @@ static long saturating_add(long a, long b)
 xdchange_t *xdl_get_hunk(xdchange_t **xscr, xdemitconf_t const *xecfg)
 {
 	xdchange_t *xch, *xchp, *lxch;
-	long max_common = saturating_add(saturating_add(xecfg->ctxlen,
-							xecfg->ctxlen),
-					 xecfg->interhunkctxlen);
-	long max_ignorable = xecfg->ctxlen;
+	long max_common;
+	long max_ignorable;
 	long ignored = 0; /* number of ignored blank lines */
 
+	if (xecfg->ctxlen < 0)
+		BUG("negative context length: %ld", xecfg->ctxlen);
+	if (xecfg->interhunkctxlen < 0)
+		BUG("negative inter-hunk context length: %ld", xecfg->interhunkctxlen);
+
+	max_common = saturating_add(saturating_add(xecfg->ctxlen,
+						   xecfg->ctxlen),
+				    xecfg->interhunkctxlen);
+	max_ignorable = xecfg->ctxlen;
+
 	/* remove ignorable changes that are too far before other changes */
 	for (xchp = *xscr; xchp && xchp->ignore; xchp = xchp->next) {
 		xch = xchp->next;
diff --git a/xdiff/xmerge.c b/xdiff/xmerge.c
index 29dad98..659ad4e 100644
--- a/xdiff/xmerge.c
+++ b/xdiff/xmerge.c
@@ -199,9 +199,9 @@ static int fill_conflict_hunk(xdfenv_t *xe1, const char *name1,
 			      int size, int i, int style,
 			      xdmerge_t *m, char *dest, int marker_size)
 {
-	int marker1_size = (name1 ? strlen(name1) + 1 : 0);
-	int marker2_size = (name2 ? strlen(name2) + 1 : 0);
-	int marker3_size = (name3 ? strlen(name3) + 1 : 0);
+	int marker1_size = (name1 && *name1 ? strlen(name1) + 1 : 0);
+	int marker2_size = (name2 && *name2 ? strlen(name2) + 1 : 0);
+	int marker3_size = (name3 && *name3 ? strlen(name3) + 1 : 0);
 	int needs_cr = is_cr_needed(xe1, xe2, m);
 
 	if (marker_size <= 0)
diff --git a/xdiff/xprepare.c b/xdiff/xprepare.c
index cd4fc40..11bada2 100644
--- a/xdiff/xprepare.c
+++ b/xdiff/xprepare.c
@@ -171,12 +171,6 @@ static int xdl_prepare_ctx(unsigned int pass, mmfile_t *mf, long narec, xpparam_
 	if (!XDL_CALLOC_ARRAY(xdf->changed, xdf->nrec + 2))
 		goto abort;
 
-	if ((XDF_DIFF_ALG(xpp->flags) != XDF_PATIENCE_DIFF) &&
-	    (XDF_DIFF_ALG(xpp->flags) != XDF_HISTOGRAM_DIFF)) {
-		if (!XDL_ALLOC_ARRAY(xdf->reference_index, xdf->nrec + 1))
-			goto abort;
-	}
-
 	xdf->changed += 1;
 	xdf->nreff = 0;
 	xdf->dstart = 0;
@@ -197,8 +191,9 @@ void xdl_free_env(xdfenv_t *xe) {
 }
 
 
-static bool xdl_clean_mmatch(uint8_t const *action, long i, long s, long e) {
-	long r, rdis0, rpdis0, rdis1, rpdis1;
+static bool xdl_clean_mmatch(uint8_t const *action, ptrdiff_t i, ptrdiff_t len) {
+	ptrdiff_t r, rdis0, rpdis0, rdis1, rpdis1;
+	ptrdiff_t s = 0, e = len - 1;
 
 	/*
 	 * Limits the window that is examined during the similar-lines
@@ -268,22 +263,24 @@ static bool xdl_clean_mmatch(uint8_t const *action, long i, long s, long e) {
  * might be potentially discarded if they appear in a run of discardable.
  */
 static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xdf2) {
-	long i, nm, mlim;
-	xrecord_t *recs;
+	ptrdiff_t i, nm, mlim1, mlim2;
 	xdlclass_t *rcrec;
 	uint8_t *action1 = NULL, *action2 = NULL;
 	bool need_min = !!(cf->flags & XDF_NEED_MINIMAL);
 	int ret = 0;
+	ptrdiff_t off = xdf1->dstart;
+	ptrdiff_t len1 = xdf1->dend - off + 1;
+	ptrdiff_t len2 = xdf2->dend - off + 1;
 
 	/*
 	 * Create temporary arrays that will help us decide if
 	 * changed[i] should remain false, or become true.
 	 */
-	if (!XDL_CALLOC_ARRAY(action1, xdf1->nrec + 1)) {
-		ret = -1;
-		goto cleanup;
-	}
-	if (!XDL_CALLOC_ARRAY(action2, xdf2->nrec + 1)) {
+	if (!XDL_CALLOC_ARRAY(action1, len1) ||
+	    !XDL_CALLOC_ARRAY(action2, len2) ||
+	    !XDL_ALLOC_ARRAY(xdf1->reference_index, len1) ||
+	    !XDL_ALLOC_ARRAY(xdf2->reference_index, len2))
+	{
 		ret = -1;
 		goto cleanup;
 	}
@@ -291,20 +288,44 @@ static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xd
 	/*
 	 * Initialize temporary arrays with DISCARD, KEEP, or INVESTIGATE.
 	 */
-	if ((mlim = xdl_bogosqrt((long)xdf1->nrec)) > XDL_MAX_EQLIMIT)
-		mlim = XDL_MAX_EQLIMIT;
-	for (i = xdf1->dstart, recs = &xdf1->recs[xdf1->dstart]; i <= xdf1->dend; i++, recs++) {
-		rcrec = cf->rcrecs[recs->minimal_perfect_hash];
+	if (need_min) {
+		/* i.e. infinity */
+		mlim1 = PTRDIFF_MAX;
+	} else {
+		mlim1 = xdl_bogosqrt((uint64_t)xdf1->nrec);
+		if (mlim1 > XDL_MAX_EQLIMIT)
+			mlim1 = XDL_MAX_EQLIMIT;
+	}
+	for (i = 0; i < len1; i++) {
+		size_t mph1 = xdf1->recs[i + off].minimal_perfect_hash;
+		rcrec = cf->rcrecs[mph1];
 		nm = rcrec ? rcrec->len2 : 0;
-		action1[i] = (nm == 0) ? DISCARD: (nm >= mlim && !need_min) ? INVESTIGATE: KEEP;
+		if (nm == 0)
+			action1[i] = DISCARD;
+		else if (nm < mlim1)
+			action1[i] = KEEP;
+		else /* nm >= mlim1 */
+			action1[i] = INVESTIGATE;
 	}
 
-	if ((mlim = xdl_bogosqrt((long)xdf2->nrec)) > XDL_MAX_EQLIMIT)
-		mlim = XDL_MAX_EQLIMIT;
-	for (i = xdf2->dstart, recs = &xdf2->recs[xdf2->dstart]; i <= xdf2->dend; i++, recs++) {
-		rcrec = cf->rcrecs[recs->minimal_perfect_hash];
+	if (need_min) {
+		/* i.e. infinity */
+		mlim2 = PTRDIFF_MAX;
+	} else {
+		mlim2 = xdl_bogosqrt((uint64_t)xdf2->nrec);
+		if (mlim2 > XDL_MAX_EQLIMIT)
+			mlim2 = XDL_MAX_EQLIMIT;
+	}
+	for (i = 0; i < len2; i++) {
+		size_t mph2 = xdf2->recs[i + off].minimal_perfect_hash;
+		rcrec = cf->rcrecs[mph2];
 		nm = rcrec ? rcrec->len1 : 0;
-		action2[i] = (nm == 0) ? DISCARD: (nm >= mlim && !need_min) ? INVESTIGATE: KEEP;
+		if (nm == 0)
+			action2[i] = DISCARD;
+		else if (nm < mlim2)
+			action2[i] = KEEP;
+		else /* nm >= mlim2 */
+			action2[i] = INVESTIGATE;
 	}
 
 	/*
@@ -312,27 +333,45 @@ static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xd
 	 * false, or become true.
 	 */
 	xdf1->nreff = 0;
-	for (i = xdf1->dstart, recs = &xdf1->recs[xdf1->dstart];
-	     i <= xdf1->dend; i++, recs++) {
-		if (action1[i] == KEEP ||
-		    (action1[i] == INVESTIGATE && !xdl_clean_mmatch(action1, i, xdf1->dstart, xdf1->dend))) {
-			xdf1->reference_index[xdf1->nreff++] = i;
-			/* changed[i] remains false, i.e. keep */
-		} else
-			xdf1->changed[i] = true;
-			/* i.e. discard */
+	for (i = 0; i < len1; i++) {
+		uint8_t action = action1[i];
+
+		if (action == INVESTIGATE) {
+			if (!xdl_clean_mmatch(action1, i, len1))
+				action = KEEP;
+			else
+				action = DISCARD;
+		}
+
+		if (action == KEEP) {
+			xdf1->reference_index[xdf1->nreff++] = i + off;
+			/* changed[i] remains false */
+		} else if (action == DISCARD) {
+			xdf1->changed[i + off] = true;
+		} else {
+			BUG("Illegal state for action");
+		}
 	}
 
 	xdf2->nreff = 0;
-	for (i = xdf2->dstart, recs = &xdf2->recs[xdf2->dstart];
-	     i <= xdf2->dend; i++, recs++) {
-		if (action2[i] == KEEP ||
-		    (action2[i] == INVESTIGATE && !xdl_clean_mmatch(action2, i, xdf2->dstart, xdf2->dend))) {
-			xdf2->reference_index[xdf2->nreff++] = i;
-			/* changed[i] remains false, i.e. keep */
-		} else
-			xdf2->changed[i] = true;
-			/* i.e. discard */
+	for (i = 0; i < len2; i++) {
+		uint8_t action = action2[i];
+
+		if (action == INVESTIGATE) {
+			if (!xdl_clean_mmatch(action2, i, len2))
+				action = KEEP;
+			else
+				action = DISCARD;
+		}
+
+		if (action == KEEP) {
+			xdf2->reference_index[xdf2->nreff++] = i + off;
+			/* changed[i] remains false */
+		} else if (action == DISCARD) {
+			xdf2->changed[i + off] = true;
+		} else {
+			BUG("Illegal state for action");
+		}
 	}
 
 cleanup:
diff --git a/xdiff/xutils.c b/xdiff/xutils.c
index 77ee1ad..9a999ac 100644
--- a/xdiff/xutils.c
+++ b/xdiff/xutils.c
@@ -23,8 +23,8 @@
 #include "xinclude.h"
 
 
-long xdl_bogosqrt(long n) {
-	long i;
+uint64_t xdl_bogosqrt(uint64_t n) {
+	uint64_t i;
 
 	/*
 	 * Classical integer square root approximation using shifts.
diff --git a/xdiff/xutils.h b/xdiff/xutils.h
index 615b4a9..58f9d74 100644
--- a/xdiff/xutils.h
+++ b/xdiff/xutils.h
@@ -25,7 +25,7 @@
 
 
 
-long xdl_bogosqrt(long n);
+uint64_t xdl_bogosqrt(uint64_t n);
 int xdl_emit_diffrec(char const *rec, long size, char const *pre, long psize,
 		     xdemitcb_t *ecb);
 int xdl_cha_init(chastore_t *cha, long isize, long icount);